update docs
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets, function(s) {
10                     if ( s.href  && s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })(); /*
16  * Based on:
17  * Ext JS Library 1.1.1
18  * Copyright(c) 2006-2007, Ext JS, LLC.
19  *
20  * Originally Released Under LGPL - original licence link has changed is not relivant.
21  *
22  * Fork - LGPL
23  * <script type="text/javascript">
24  */
25
26
27 /**
28  * @class Roo.Shadow
29  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
30  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
31  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
32  * @constructor
33  * Create a new Shadow
34  * @param {Object} config The config object
35  */
36 Roo.Shadow = function(config){
37     Roo.apply(this, config);
38     if(typeof this.mode != "string"){
39         this.mode = this.defaultMode;
40     }
41     var o = this.offset, a = {h: 0};
42     var rad = Math.floor(this.offset/2);
43     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
44         case "drop":
45             a.w = 0;
46             a.l = a.t = o;
47             a.t -= 1;
48             if(Roo.isIE){
49                 a.l -= this.offset + rad;
50                 a.t -= this.offset + rad;
51                 a.w -= rad;
52                 a.h -= rad;
53                 a.t += 1;
54             }
55         break;
56         case "sides":
57             a.w = (o*2);
58             a.l = -o;
59             a.t = o-1;
60             if(Roo.isIE){
61                 a.l -= (this.offset - rad);
62                 a.t -= this.offset + rad;
63                 a.l += 1;
64                 a.w -= (this.offset - rad)*2;
65                 a.w -= rad + 1;
66                 a.h -= 1;
67             }
68         break;
69         case "frame":
70             a.w = a.h = (o*2);
71             a.l = a.t = -o;
72             a.t += 1;
73             a.h -= 2;
74             if(Roo.isIE){
75                 a.l -= (this.offset - rad);
76                 a.t -= (this.offset - rad);
77                 a.l += 1;
78                 a.w -= (this.offset + rad + 1);
79                 a.h -= (this.offset + rad);
80                 a.h += 1;
81             }
82         break;
83     };
84
85     this.adjusts = a;
86 };
87
88 Roo.Shadow.prototype = {
89     /**
90      * @cfg {String} mode
91      * The shadow display mode.  Supports the following options:<br />
92      * sides: Shadow displays on both sides and bottom only<br />
93      * frame: Shadow displays equally on all four sides<br />
94      * drop: Traditional bottom-right drop shadow (default)
95      */
96     /**
97      * @cfg {String} offset
98      * The number of pixels to offset the shadow from the element (defaults to 4)
99      */
100     offset: 4,
101
102     // private
103     defaultMode: "drop",
104
105     /**
106      * Displays the shadow under the target element
107      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
108      */
109     show : function(target){
110         target = Roo.get(target);
111         if(!this.el){
112             this.el = Roo.Shadow.Pool.pull();
113             if(this.el.dom.nextSibling != target.dom){
114                 this.el.insertBefore(target);
115             }
116         }
117         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
118         if(Roo.isIE){
119             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
120         }
121         this.realign(
122             target.getLeft(true),
123             target.getTop(true),
124             target.getWidth(),
125             target.getHeight()
126         );
127         this.el.dom.style.display = "block";
128     },
129
130     /**
131      * Returns true if the shadow is visible, else false
132      */
133     isVisible : function(){
134         return this.el ? true : false;  
135     },
136
137     /**
138      * Direct alignment when values are already available. Show must be called at least once before
139      * calling this method to ensure it is initialized.
140      * @param {Number} left The target element left position
141      * @param {Number} top The target element top position
142      * @param {Number} width The target element width
143      * @param {Number} height The target element height
144      */
145     realign : function(l, t, w, h){
146         if(!this.el){
147             return;
148         }
149         var a = this.adjusts, d = this.el.dom, s = d.style;
150         var iea = 0;
151         s.left = (l+a.l)+"px";
152         s.top = (t+a.t)+"px";
153         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
154  
155         if(s.width != sws || s.height != shs){
156             s.width = sws;
157             s.height = shs;
158             if(!Roo.isIE){
159                 var cn = d.childNodes;
160                 var sww = Math.max(0, (sw-12))+"px";
161                 cn[0].childNodes[1].style.width = sww;
162                 cn[1].childNodes[1].style.width = sww;
163                 cn[2].childNodes[1].style.width = sww;
164                 cn[1].style.height = Math.max(0, (sh-12))+"px";
165             }
166         }
167     },
168
169     /**
170      * Hides this shadow
171      */
172     hide : function(){
173         if(this.el){
174             this.el.dom.style.display = "none";
175             Roo.Shadow.Pool.push(this.el);
176             delete this.el;
177         }
178     },
179
180     /**
181      * Adjust the z-index of this shadow
182      * @param {Number} zindex The new z-index
183      */
184     setZIndex : function(z){
185         this.zIndex = z;
186         if(this.el){
187             this.el.setStyle("z-index", z);
188         }
189     }
190 };
191
192 // Private utility class that manages the internal Shadow cache
193 Roo.Shadow.Pool = function(){
194     var p = [];
195     var markup = Roo.isIE ?
196                  '<div class="x-ie-shadow"></div>' :
197                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
198     return {
199         pull : function(){
200             var sh = p.shift();
201             if(!sh){
202                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
203                 sh.autoBoxAdjust = false;
204             }
205             return sh;
206         },
207
208         push : function(sh){
209             p.push(sh);
210         }
211     };
212 }();/*
213  * - LGPL
214  *
215  * base class for bootstrap elements.
216  * 
217  */
218
219 Roo.bootstrap = Roo.bootstrap || {};
220 /**
221  * @class Roo.bootstrap.Component
222  * @extends Roo.Component
223  * Bootstrap Component base class
224  * @cfg {String} cls css class
225  * @cfg {String} style any extra css
226  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
227  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
228  * @cfg {string} dataId cutomer id
229  * @cfg {string} name Specifies name attribute
230  * @cfg {string} tooltip  Text for the tooltip
231  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
232  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
233  
234  * @constructor
235  * Do not use directly - it does not do anything..
236  * @param {Object} config The config object
237  */
238
239
240
241 Roo.bootstrap.Component = function(config){
242     Roo.bootstrap.Component.superclass.constructor.call(this, config);
243        
244     this.addEvents({
245         /**
246          * @event childrenrendered
247          * Fires when the children have been rendered..
248          * @param {Roo.bootstrap.Component} this
249          */
250         "childrenrendered" : true
251         
252         
253         
254     });
255     
256     
257 };
258
259 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
260     
261     
262     allowDomMove : false, // to stop relocations in parent onRender...
263     
264     cls : false,
265     
266     style : false,
267     
268     autoCreate : false,
269     
270     tooltip : null,
271     /**
272      * Initialize Events for the element
273      */
274     initEvents : function() { },
275     
276     xattr : false,
277     
278     parentId : false,
279     
280     can_build_overlaid : true,
281     
282     container_method : false,
283     
284     dataId : false,
285     
286     name : false,
287     
288     parent: function() {
289         // returns the parent component..
290         return Roo.ComponentMgr.get(this.parentId)
291         
292         
293     },
294     
295     // private
296     onRender : function(ct, position)
297     {
298        // Roo.log("Call onRender: " + this.xtype);
299         
300         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
301         
302         if(this.el){
303             if (this.el.attr('xtype')) {
304                 this.el.attr('xtypex', this.el.attr('xtype'));
305                 this.el.dom.removeAttribute('xtype');
306                 
307                 this.initEvents();
308             }
309             
310             return;
311         }
312         
313          
314         
315         var cfg = Roo.apply({},  this.getAutoCreate());
316         
317         cfg.id = this.id || Roo.id();
318         
319         // fill in the extra attributes 
320         if (this.xattr && typeof(this.xattr) =='object') {
321             for (var i in this.xattr) {
322                 cfg[i] = this.xattr[i];
323             }
324         }
325         
326         if(this.dataId){
327             cfg.dataId = this.dataId;
328         }
329         
330         if (this.cls) {
331             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
332         }
333         
334         if (this.style) { // fixme needs to support more complex style data.
335             cfg.style = this.style;
336         }
337         
338         if(this.name){
339             cfg.name = this.name;
340         }
341         
342         this.el = ct.createChild(cfg, position);
343         
344         if (this.tooltip) {
345             this.tooltipEl().attr('tooltip', this.tooltip);
346         }
347         
348         if(this.tabIndex !== undefined){
349             this.el.dom.setAttribute('tabIndex', this.tabIndex);
350         }
351         
352         this.initEvents();
353         
354     },
355     /**
356      * Fetch the element to add children to
357      * @return {Roo.Element} defaults to this.el
358      */
359     getChildContainer : function()
360     {
361         return this.el;
362     },
363     /**
364      * Fetch the element to display the tooltip on.
365      * @return {Roo.Element} defaults to this.el
366      */
367     tooltipEl : function()
368     {
369         return this.el;
370     },
371         
372     addxtype  : function(tree,cntr)
373     {
374         var cn = this;
375         
376         cn = Roo.factory(tree);
377         //Roo.log(['addxtype', cn]);
378            
379         cn.parentType = this.xtype; //??
380         cn.parentId = this.id;
381         
382         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
383         if (typeof(cn.container_method) == 'string') {
384             cntr = cn.container_method;
385         }
386         
387         
388         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
389         
390         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
391         
392         var build_from_html =  Roo.XComponent.build_from_html;
393           
394         var is_body  = (tree.xtype == 'Body') ;
395           
396         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
397           
398         var self_cntr_el = Roo.get(this[cntr](false));
399         
400         // do not try and build conditional elements 
401         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
402             return false;
403         }
404         
405         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
406             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
407                 return this.addxtypeChild(tree,cntr, is_body);
408             }
409             
410             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
411                 
412             if(echild){
413                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
414             }
415             
416             Roo.log('skipping render');
417             return cn;
418             
419         }
420         
421         var ret = false;
422         if (!build_from_html) {
423             return false;
424         }
425         
426         // this i think handles overlaying multiple children of the same type
427         // with the sam eelement.. - which might be buggy..
428         while (true) {
429             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
430             
431             if (!echild) {
432                 break;
433             }
434             
435             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
436                 break;
437             }
438             
439             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
440         }
441        
442         return ret;
443     },
444     
445     
446     addxtypeChild : function (tree, cntr, is_body)
447     {
448         Roo.debug && Roo.log('addxtypeChild:' + cntr);
449         var cn = this;
450         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
451         
452         
453         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
454                     (typeof(tree['flexy:foreach']) != 'undefined');
455           
456     
457         
458         skip_children = false;
459         // render the element if it's not BODY.
460         if (!is_body) {
461             
462             // if parent was disabled, then do not try and create the children..
463             if(!this[cntr](true)){
464                 tree.items = [];
465                 return tree;
466             }
467            
468             cn = Roo.factory(tree);
469            
470             cn.parentType = this.xtype; //??
471             cn.parentId = this.id;
472             
473             var build_from_html =  Roo.XComponent.build_from_html;
474             
475             
476             // does the container contain child eleemnts with 'xtype' attributes.
477             // that match this xtype..
478             // note - when we render we create these as well..
479             // so we should check to see if body has xtype set.
480             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
481                
482                 var self_cntr_el = Roo.get(this[cntr](false));
483                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
484                 if (echild) { 
485                     //Roo.log(Roo.XComponent.build_from_html);
486                     //Roo.log("got echild:");
487                     //Roo.log(echild);
488                 }
489                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
490                 // and are not displayed -this causes this to use up the wrong element when matching.
491                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
492                 
493                 
494                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
495                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
496                   
497                   
498                   
499                     cn.el = echild;
500                   //  Roo.log("GOT");
501                     //echild.dom.removeAttribute('xtype');
502                 } else {
503                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
504                     Roo.debug && Roo.log(self_cntr_el);
505                     Roo.debug && Roo.log(echild);
506                     Roo.debug && Roo.log(cn);
507                 }
508             }
509            
510             
511            
512             // if object has flexy:if - then it may or may not be rendered.
513             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
514                 // skip a flexy if element.
515                 Roo.debug && Roo.log('skipping render');
516                 Roo.debug && Roo.log(tree);
517                 if (!cn.el) {
518                     Roo.debug && Roo.log('skipping all children');
519                     skip_children = true;
520                 }
521                 
522              } else {
523                  
524                 // actually if flexy:foreach is found, we really want to create 
525                 // multiple copies here...
526                 //Roo.log('render');
527                 //Roo.log(this[cntr]());
528                 // some elements do not have render methods.. like the layouts...
529                 /*
530                 if(this[cntr](true) === false){
531                     cn.items = [];
532                     return cn;
533                 }
534                 */
535                 cn.render && cn.render(this[cntr](true));
536                 
537              }
538             // then add the element..
539         }
540          
541         // handle the kids..
542         
543         var nitems = [];
544         /*
545         if (typeof (tree.menu) != 'undefined') {
546             tree.menu.parentType = cn.xtype;
547             tree.menu.triggerEl = cn.el;
548             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
549             
550         }
551         */
552         if (!tree.items || !tree.items.length) {
553             cn.items = nitems;
554             //Roo.log(["no children", this]);
555             
556             return cn;
557         }
558          
559         var items = tree.items;
560         delete tree.items;
561         
562         //Roo.log(items.length);
563             // add the items..
564         if (!skip_children) {    
565             for(var i =0;i < items.length;i++) {
566               //  Roo.log(['add child', items[i]]);
567                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
568             }
569         }
570         
571         cn.items = nitems;
572         
573         //Roo.log("fire childrenrendered");
574         
575         cn.fireEvent('childrenrendered', this);
576         
577         return cn;
578     },
579     
580     /**
581      * Set the element that will be used to show or hide
582      */
583     setVisibilityEl : function(el)
584     {
585         this.visibilityEl = el;
586     },
587     
588      /**
589      * Get the element that will be used to show or hide
590      */
591     getVisibilityEl : function()
592     {
593         if (typeof(this.visibilityEl) == 'object') {
594             return this.visibilityEl;
595         }
596         
597         if (typeof(this.visibilityEl) == 'string') {
598             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
599         }
600         
601         return this.getEl();
602     },
603     
604     /**
605      * Show a component - removes 'hidden' class
606      */
607     show : function()
608     {
609         if(!this.getVisibilityEl()){
610             return;
611         }
612          
613         this.getVisibilityEl().removeClass(['hidden','d-none']);
614         
615         this.fireEvent('show', this);
616         
617         
618     },
619     /**
620      * Hide a component - adds 'hidden' class
621      */
622     hide: function()
623     {
624         if(!this.getVisibilityEl()){
625             return;
626         }
627         
628         this.getVisibilityEl().addClass(['hidden','d-none']);
629         
630         this.fireEvent('hide', this);
631         
632     }
633 });
634
635  /*
636  * - LGPL
637  *
638  * element
639  * 
640  */
641
642 /**
643  * @class Roo.bootstrap.Element
644  * @extends Roo.bootstrap.Component
645  * Bootstrap Element class
646  * @cfg {String} html contents of the element
647  * @cfg {String} tag tag of the element
648  * @cfg {String} cls class of the element
649  * @cfg {Boolean} preventDefault (true|false) default false
650  * @cfg {Boolean} clickable (true|false) default false
651  * 
652  * @constructor
653  * Create a new Element
654  * @param {Object} config The config object
655  */
656
657 Roo.bootstrap.Element = function(config){
658     Roo.bootstrap.Element.superclass.constructor.call(this, config);
659     
660     this.addEvents({
661         // raw events
662         /**
663          * @event click
664          * When a element is chick
665          * @param {Roo.bootstrap.Element} this
666          * @param {Roo.EventObject} e
667          */
668         "click" : true
669     });
670 };
671
672 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
673     
674     tag: 'div',
675     cls: '',
676     html: '',
677     preventDefault: false, 
678     clickable: false,
679     
680     getAutoCreate : function(){
681         
682         var cfg = {
683             tag: this.tag,
684             // cls: this.cls, double assign in parent class Component.js :: onRender
685             html: this.html
686         };
687         
688         return cfg;
689     },
690     
691     initEvents: function() 
692     {
693         Roo.bootstrap.Element.superclass.initEvents.call(this);
694         
695         if(this.clickable){
696             this.el.on('click', this.onClick, this);
697         }
698         
699     },
700     
701     onClick : function(e)
702     {
703         if(this.preventDefault){
704             e.preventDefault();
705         }
706         
707         this.fireEvent('click', this, e);
708     },
709     
710     getValue : function()
711     {
712         return this.el.dom.innerHTML;
713     },
714     
715     setValue : function(value)
716     {
717         this.el.dom.innerHTML = value;
718     }
719    
720 });
721
722  
723
724  /*
725  * - LGPL
726  *
727  * dropable area
728  * 
729  */
730
731 /**
732  * @class Roo.bootstrap.DropTarget
733  * @extends Roo.bootstrap.Element
734  * Bootstrap DropTarget class
735  
736  * @cfg {string} name dropable name
737  * 
738  * @constructor
739  * Create a new Dropable Area
740  * @param {Object} config The config object
741  */
742
743 Roo.bootstrap.DropTarget = function(config){
744     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
745     
746     this.addEvents({
747         // raw events
748         /**
749          * @event click
750          * When a element is chick
751          * @param {Roo.bootstrap.Element} this
752          * @param {Roo.EventObject} e
753          */
754         "drop" : true
755     });
756 };
757
758 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
759     
760     
761     getAutoCreate : function(){
762         
763          
764     },
765     
766     initEvents: function() 
767     {
768         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
769         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
770             ddGroup: this.name,
771             listeners : {
772                 drop : this.dragDrop.createDelegate(this),
773                 enter : this.dragEnter.createDelegate(this),
774                 out : this.dragOut.createDelegate(this),
775                 over : this.dragOver.createDelegate(this)
776             }
777             
778         });
779         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
780     },
781     
782     dragDrop : function(source,e,data)
783     {
784         // user has to decide how to impliment this.
785         Roo.log('drop');
786         Roo.log(this);
787         //this.fireEvent('drop', this, source, e ,data);
788         return false;
789     },
790     
791     dragEnter : function(n, dd, e, data)
792     {
793         // probably want to resize the element to match the dropped element..
794         Roo.log("enter");
795         this.originalSize = this.el.getSize();
796         this.el.setSize( n.el.getSize());
797         this.dropZone.DDM.refreshCache(this.name);
798         Roo.log([n, dd, e, data]);
799     },
800     
801     dragOut : function(value)
802     {
803         // resize back to normal
804         Roo.log("out");
805         this.el.setSize(this.originalSize);
806         this.dropZone.resetConstraints();
807     },
808     
809     dragOver : function()
810     {
811         // ??? do nothing?
812     }
813    
814 });
815
816  
817
818  /*
819  * - LGPL
820  *
821  * Body
822  *
823  */
824
825 /**
826  * @class Roo.bootstrap.Body
827  * @extends Roo.bootstrap.Component
828  * Bootstrap Body class
829  *
830  * @constructor
831  * Create a new body
832  * @param {Object} config The config object
833  */
834
835 Roo.bootstrap.Body = function(config){
836
837     config = config || {};
838
839     Roo.bootstrap.Body.superclass.constructor.call(this, config);
840     this.el = Roo.get(config.el ? config.el : document.body );
841     if (this.cls && this.cls.length) {
842         Roo.get(document.body).addClass(this.cls);
843     }
844 };
845
846 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
847
848     is_body : true,// just to make sure it's constructed?
849
850         autoCreate : {
851         cls: 'container'
852     },
853     onRender : function(ct, position)
854     {
855        /* Roo.log("Roo.bootstrap.Body - onRender");
856         if (this.cls && this.cls.length) {
857             Roo.get(document.body).addClass(this.cls);
858         }
859         // style??? xttr???
860         */
861     }
862
863
864
865
866 });
867 /*
868  * - LGPL
869  *
870  * button group
871  * 
872  */
873
874
875 /**
876  * @class Roo.bootstrap.ButtonGroup
877  * @extends Roo.bootstrap.Component
878  * Bootstrap ButtonGroup class
879  * @cfg {String} size lg | sm | xs (default empty normal)
880  * @cfg {String} align vertical | justified  (default none)
881  * @cfg {String} direction up | down (default down)
882  * @cfg {Boolean} toolbar false | true
883  * @cfg {Boolean} btn true | false
884  * 
885  * 
886  * @constructor
887  * Create a new Input
888  * @param {Object} config The config object
889  */
890
891 Roo.bootstrap.ButtonGroup = function(config){
892     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
893 };
894
895 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
896     
897     size: '',
898     align: '',
899     direction: '',
900     toolbar: false,
901     btn: true,
902
903     getAutoCreate : function(){
904         var cfg = {
905             cls: 'btn-group',
906             html : null
907         };
908         
909         cfg.html = this.html || cfg.html;
910         
911         if (this.toolbar) {
912             cfg = {
913                 cls: 'btn-toolbar',
914                 html: null
915             };
916             
917             return cfg;
918         }
919         
920         if (['vertical','justified'].indexOf(this.align)!==-1) {
921             cfg.cls = 'btn-group-' + this.align;
922             
923             if (this.align == 'justified') {
924                 console.log(this.items);
925             }
926         }
927         
928         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
929             cfg.cls += ' btn-group-' + this.size;
930         }
931         
932         if (this.direction == 'up') {
933             cfg.cls += ' dropup' ;
934         }
935         
936         return cfg;
937     },
938     /**
939      * Add a button to the group (similar to NavItem API.)
940      */
941     addItem : function(cfg)
942     {
943         var cn = new Roo.bootstrap.Button(cfg);
944         //this.register(cn);
945         cn.parentId = this.id;
946         cn.onRender(this.el, null);
947         return cn;
948     }
949    
950 });
951
952  /*
953  * - LGPL
954  *
955  * button
956  * 
957  */
958
959 /**
960  * @class Roo.bootstrap.Button
961  * @extends Roo.bootstrap.Component
962  * Bootstrap Button class
963  * @cfg {String} html The button content
964  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
965  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
966  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
967  * @cfg {String} size (lg|sm|xs)
968  * @cfg {String} tag (a|input|submit)
969  * @cfg {String} href empty or href
970  * @cfg {Boolean} disabled default false;
971  * @cfg {Boolean} isClose default false;
972  * @cfg {String} glyphicon depricated - use fa
973  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
974  * @cfg {String} badge text for badge
975  * @cfg {String} theme (default|glow)  
976  * @cfg {Boolean} inverse dark themed version
977  * @cfg {Boolean} toggle is it a slidy toggle button
978  * @cfg {Boolean} pressed   default null - if the button ahs active state
979  * @cfg {String} ontext text for on slidy toggle state
980  * @cfg {String} offtext text for off slidy toggle state
981  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
982  * @cfg {Boolean} removeClass remove the standard class..
983  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
984  * 
985  * @constructor
986  * Create a new button
987  * @param {Object} config The config object
988  */
989
990
991 Roo.bootstrap.Button = function(config){
992     Roo.bootstrap.Button.superclass.constructor.call(this, config);
993     
994     this.addEvents({
995         // raw events
996         /**
997          * @event click
998          * When a butotn is pressed
999          * @param {Roo.bootstrap.Button} btn
1000          * @param {Roo.EventObject} e
1001          */
1002         "click" : true,
1003          /**
1004          * @event toggle
1005          * After the button has been toggles
1006          * @param {Roo.bootstrap.Button} btn
1007          * @param {Roo.EventObject} e
1008          * @param {boolean} pressed (also available as button.pressed)
1009          */
1010         "toggle" : true
1011     });
1012 };
1013
1014 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1015     html: false,
1016     active: false,
1017     weight: '',
1018     badge_weight: '',
1019     outline : false,
1020     size: '',
1021     tag: 'button',
1022     href: '',
1023     disabled: false,
1024     isClose: false,
1025     glyphicon: '',
1026     fa: '',
1027     badge: '',
1028     theme: 'default',
1029     inverse: false,
1030     
1031     toggle: false,
1032     ontext: 'ON',
1033     offtext: 'OFF',
1034     defaulton: true,
1035     preventDefault: true,
1036     removeClass: false,
1037     name: false,
1038     target: false,
1039      
1040     pressed : null,
1041      
1042     
1043     getAutoCreate : function(){
1044         
1045         var cfg = {
1046             tag : 'button',
1047             cls : 'roo-button',
1048             html: ''
1049         };
1050         
1051         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1052             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1053             this.tag = 'button';
1054         } else {
1055             cfg.tag = this.tag;
1056         }
1057         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1058         
1059         if (this.toggle == true) {
1060             cfg={
1061                 tag: 'div',
1062                 cls: 'slider-frame roo-button',
1063                 cn: [
1064                     {
1065                         tag: 'span',
1066                         'data-on-text':'ON',
1067                         'data-off-text':'OFF',
1068                         cls: 'slider-button',
1069                         html: this.offtext
1070                     }
1071                 ]
1072             };
1073             // why are we validating the weights?
1074             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1075                 cfg.cls +=  ' ' + this.weight;
1076             }
1077             
1078             return cfg;
1079         }
1080         
1081         if (this.isClose) {
1082             cfg.cls += ' close';
1083             
1084             cfg["aria-hidden"] = true;
1085             
1086             cfg.html = "&times;";
1087             
1088             return cfg;
1089         }
1090              
1091         
1092         if (this.theme==='default') {
1093             cfg.cls = 'btn roo-button';
1094             
1095             //if (this.parentType != 'Navbar') {
1096             this.weight = this.weight.length ?  this.weight : 'default';
1097             //}
1098             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1099                 
1100                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1101                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1102                 cfg.cls += ' btn-' + outline + weight;
1103                 if (this.weight == 'default') {
1104                     // BC
1105                     cfg.cls += ' btn-' + this.weight;
1106                 }
1107             }
1108         } else if (this.theme==='glow') {
1109             
1110             cfg.tag = 'a';
1111             cfg.cls = 'btn-glow roo-button';
1112             
1113             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1114                 
1115                 cfg.cls += ' ' + this.weight;
1116             }
1117         }
1118    
1119         
1120         if (this.inverse) {
1121             this.cls += ' inverse';
1122         }
1123         
1124         
1125         if (this.active || this.pressed === true) {
1126             cfg.cls += ' active';
1127         }
1128         
1129         if (this.disabled) {
1130             cfg.disabled = 'disabled';
1131         }
1132         
1133         if (this.items) {
1134             Roo.log('changing to ul' );
1135             cfg.tag = 'ul';
1136             this.glyphicon = 'caret';
1137             if (Roo.bootstrap.version == 4) {
1138                 this.fa = 'caret-down';
1139             }
1140             
1141         }
1142         
1143         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1144          
1145         //gsRoo.log(this.parentType);
1146         if (this.parentType === 'Navbar' && !this.parent().bar) {
1147             Roo.log('changing to li?');
1148             
1149             cfg.tag = 'li';
1150             
1151             cfg.cls = '';
1152             cfg.cn =  [{
1153                 tag : 'a',
1154                 cls : 'roo-button',
1155                 html : this.html,
1156                 href : this.href || '#'
1157             }];
1158             if (this.menu) {
1159                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1160                 cfg.cls += ' dropdown';
1161             }   
1162             
1163             delete cfg.html;
1164             
1165         }
1166         
1167        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1168         
1169         if (this.glyphicon) {
1170             cfg.html = ' ' + cfg.html;
1171             
1172             cfg.cn = [
1173                 {
1174                     tag: 'span',
1175                     cls: 'glyphicon glyphicon-' + this.glyphicon
1176                 }
1177             ];
1178         }
1179         if (this.fa) {
1180             cfg.html = ' ' + cfg.html;
1181             
1182             cfg.cn = [
1183                 {
1184                     tag: 'i',
1185                     cls: 'fa fas fa-' + this.fa
1186                 }
1187             ];
1188         }
1189         
1190         if (this.badge) {
1191             cfg.html += ' ';
1192             
1193             cfg.tag = 'a';
1194             
1195 //            cfg.cls='btn roo-button';
1196             
1197             cfg.href=this.href;
1198             
1199             var value = cfg.html;
1200             
1201             if(this.glyphicon){
1202                 value = {
1203                     tag: 'span',
1204                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1205                     html: this.html
1206                 };
1207             }
1208             if(this.fa){
1209                 value = {
1210                     tag: 'i',
1211                     cls: 'fa fas fa-' + this.fa,
1212                     html: this.html
1213                 };
1214             }
1215             
1216             var bw = this.badge_weight.length ? this.badge_weight :
1217                 (this.weight.length ? this.weight : 'secondary');
1218             bw = bw == 'default' ? 'secondary' : bw;
1219             
1220             cfg.cn = [
1221                 value,
1222                 {
1223                     tag: 'span',
1224                     cls: 'badge badge-' + bw,
1225                     html: this.badge
1226                 }
1227             ];
1228             
1229             cfg.html='';
1230         }
1231         
1232         if (this.menu) {
1233             cfg.cls += ' dropdown';
1234             cfg.html = typeof(cfg.html) != 'undefined' ?
1235                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1236         }
1237         
1238         if (cfg.tag !== 'a' && this.href !== '') {
1239             throw "Tag must be a to set href.";
1240         } else if (this.href.length > 0) {
1241             cfg.href = this.href;
1242         }
1243         
1244         if(this.removeClass){
1245             cfg.cls = '';
1246         }
1247         
1248         if(this.target){
1249             cfg.target = this.target;
1250         }
1251         
1252         return cfg;
1253     },
1254     initEvents: function() {
1255        // Roo.log('init events?');
1256 //        Roo.log(this.el.dom);
1257         // add the menu...
1258         
1259         if (typeof (this.menu) != 'undefined') {
1260             this.menu.parentType = this.xtype;
1261             this.menu.triggerEl = this.el;
1262             this.addxtype(Roo.apply({}, this.menu));
1263         }
1264
1265
1266        if (this.el.hasClass('roo-button')) {
1267             this.el.on('click', this.onClick, this);
1268        } else {
1269             this.el.select('.roo-button').on('click', this.onClick, this);
1270        }
1271        
1272        if(this.removeClass){
1273            this.el.on('click', this.onClick, this);
1274        }
1275        
1276        this.el.enableDisplayMode();
1277         
1278     },
1279     onClick : function(e)
1280     {
1281         if (this.disabled) {
1282             return;
1283         }
1284         
1285         Roo.log('button on click ');
1286         if(this.preventDefault){
1287             e.preventDefault();
1288         }
1289         
1290         if (this.pressed === true || this.pressed === false) {
1291             this.toggleActive(e);
1292         }
1293         
1294         
1295         this.fireEvent('click', this, e);
1296     },
1297     
1298     /**
1299      * Enables this button
1300      */
1301     enable : function()
1302     {
1303         this.disabled = false;
1304         this.el.removeClass('disabled');
1305     },
1306     
1307     /**
1308      * Disable this button
1309      */
1310     disable : function()
1311     {
1312         this.disabled = true;
1313         this.el.addClass('disabled');
1314     },
1315      /**
1316      * sets the active state on/off, 
1317      * @param {Boolean} state (optional) Force a particular state
1318      */
1319     setActive : function(v) {
1320         
1321         this.el[v ? 'addClass' : 'removeClass']('active');
1322         this.pressed = v;
1323     },
1324      /**
1325      * toggles the current active state 
1326      */
1327     toggleActive : function(e)
1328     {
1329         this.setActive(!this.pressed);
1330         this.fireEvent('toggle', this, e, !this.pressed);
1331     },
1332      /**
1333      * get the current active state
1334      * @return {boolean} true if it's active
1335      */
1336     isActive : function()
1337     {
1338         return this.el.hasClass('active');
1339     },
1340     /**
1341      * set the text of the first selected button
1342      */
1343     setText : function(str)
1344     {
1345         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1346     },
1347     /**
1348      * get the text of the first selected button
1349      */
1350     getText : function()
1351     {
1352         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1353     },
1354     
1355     setWeight : function(str)
1356     {
1357         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1358         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1359         this.weight = str;
1360         var outline = this.outline ? 'outline-' : '';
1361         if (str == 'default') {
1362             this.el.addClass('btn-default btn-outline-secondary');        
1363             return;
1364         }
1365         this.el.addClass('btn-' + outline + str);        
1366     }
1367     
1368     
1369 });
1370 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1371
1372 Roo.bootstrap.Button.weights = [
1373     'default',
1374     'secondary' ,
1375     'primary',
1376     'success',
1377     'info',
1378     'warning',
1379     'danger',
1380     'link',
1381     'light',
1382     'dark'              
1383    
1384 ];/*
1385  * - LGPL
1386  *
1387  * column
1388  * 
1389  */
1390
1391 /**
1392  * @class Roo.bootstrap.Column
1393  * @extends Roo.bootstrap.Component
1394  * Bootstrap Column class
1395  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1396  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1397  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1398  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1399  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1400  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1401  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1402  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1403  *
1404  * 
1405  * @cfg {Boolean} hidden (true|false) hide the element
1406  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1407  * @cfg {String} fa (ban|check|...) font awesome icon
1408  * @cfg {Number} fasize (1|2|....) font awsome size
1409
1410  * @cfg {String} icon (info-sign|check|...) glyphicon name
1411
1412  * @cfg {String} html content of column.
1413  * 
1414  * @constructor
1415  * Create a new Column
1416  * @param {Object} config The config object
1417  */
1418
1419 Roo.bootstrap.Column = function(config){
1420     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1421 };
1422
1423 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1424     
1425     xs: false,
1426     sm: false,
1427     md: false,
1428     lg: false,
1429     xsoff: false,
1430     smoff: false,
1431     mdoff: false,
1432     lgoff: false,
1433     html: '',
1434     offset: 0,
1435     alert: false,
1436     fa: false,
1437     icon : false,
1438     hidden : false,
1439     fasize : 1,
1440     
1441     getAutoCreate : function(){
1442         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1443         
1444         cfg = {
1445             tag: 'div',
1446             cls: 'column'
1447         };
1448         
1449         var settings=this;
1450         var sizes =   ['xs','sm','md','lg'];
1451         sizes.map(function(size ,ix){
1452             //Roo.log( size + ':' + settings[size]);
1453             
1454             if (settings[size+'off'] !== false) {
1455                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1456             }
1457             
1458             if (settings[size] === false) {
1459                 return;
1460             }
1461             
1462             if (!settings[size]) { // 0 = hidden
1463                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1464                 // bootsrap4
1465                 for (var i = ix; i > -1; i--) {
1466                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1467                 }
1468                 
1469                 
1470                 return;
1471             }
1472             cfg.cls += ' col-' + size + '-' + settings[size] + (
1473                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1474             );
1475             
1476         });
1477         
1478         if (this.hidden) {
1479             cfg.cls += ' hidden';
1480         }
1481         
1482         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1483             cfg.cls +=' alert alert-' + this.alert;
1484         }
1485         
1486         
1487         if (this.html.length) {
1488             cfg.html = this.html;
1489         }
1490         if (this.fa) {
1491             var fasize = '';
1492             if (this.fasize > 1) {
1493                 fasize = ' fa-' + this.fasize + 'x';
1494             }
1495             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1496             
1497             
1498         }
1499         if (this.icon) {
1500             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1501         }
1502         
1503         return cfg;
1504     }
1505    
1506 });
1507
1508  
1509
1510  /*
1511  * - LGPL
1512  *
1513  * page container.
1514  * 
1515  */
1516
1517
1518 /**
1519  * @class Roo.bootstrap.Container
1520  * @extends Roo.bootstrap.Component
1521  * Bootstrap Container class
1522  * @cfg {Boolean} jumbotron is it a jumbotron element
1523  * @cfg {String} html content of element
1524  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1525  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1526  * @cfg {String} header content of header (for panel)
1527  * @cfg {String} footer content of footer (for panel)
1528  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1529  * @cfg {String} tag (header|aside|section) type of HTML tag.
1530  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1531  * @cfg {String} fa font awesome icon
1532  * @cfg {String} icon (info-sign|check|...) glyphicon name
1533  * @cfg {Boolean} hidden (true|false) hide the element
1534  * @cfg {Boolean} expandable (true|false) default false
1535  * @cfg {Boolean} expanded (true|false) default true
1536  * @cfg {String} rheader contet on the right of header
1537  * @cfg {Boolean} clickable (true|false) default false
1538
1539  *     
1540  * @constructor
1541  * Create a new Container
1542  * @param {Object} config The config object
1543  */
1544
1545 Roo.bootstrap.Container = function(config){
1546     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1547     
1548     this.addEvents({
1549         // raw events
1550          /**
1551          * @event expand
1552          * After the panel has been expand
1553          * 
1554          * @param {Roo.bootstrap.Container} this
1555          */
1556         "expand" : true,
1557         /**
1558          * @event collapse
1559          * After the panel has been collapsed
1560          * 
1561          * @param {Roo.bootstrap.Container} this
1562          */
1563         "collapse" : true,
1564         /**
1565          * @event click
1566          * When a element is chick
1567          * @param {Roo.bootstrap.Container} this
1568          * @param {Roo.EventObject} e
1569          */
1570         "click" : true
1571     });
1572 };
1573
1574 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1575     
1576     jumbotron : false,
1577     well: '',
1578     panel : '',
1579     header: '',
1580     footer : '',
1581     sticky: '',
1582     tag : false,
1583     alert : false,
1584     fa: false,
1585     icon : false,
1586     expandable : false,
1587     rheader : '',
1588     expanded : true,
1589     clickable: false,
1590   
1591      
1592     getChildContainer : function() {
1593         
1594         if(!this.el){
1595             return false;
1596         }
1597         
1598         if (this.panel.length) {
1599             return this.el.select('.panel-body',true).first();
1600         }
1601         
1602         return this.el;
1603     },
1604     
1605     
1606     getAutoCreate : function(){
1607         
1608         var cfg = {
1609             tag : this.tag || 'div',
1610             html : '',
1611             cls : ''
1612         };
1613         if (this.jumbotron) {
1614             cfg.cls = 'jumbotron';
1615         }
1616         
1617         
1618         
1619         // - this is applied by the parent..
1620         //if (this.cls) {
1621         //    cfg.cls = this.cls + '';
1622         //}
1623         
1624         if (this.sticky.length) {
1625             
1626             var bd = Roo.get(document.body);
1627             if (!bd.hasClass('bootstrap-sticky')) {
1628                 bd.addClass('bootstrap-sticky');
1629                 Roo.select('html',true).setStyle('height', '100%');
1630             }
1631              
1632             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1633         }
1634         
1635         
1636         if (this.well.length) {
1637             switch (this.well) {
1638                 case 'lg':
1639                 case 'sm':
1640                     cfg.cls +=' well well-' +this.well;
1641                     break;
1642                 default:
1643                     cfg.cls +=' well';
1644                     break;
1645             }
1646         }
1647         
1648         if (this.hidden) {
1649             cfg.cls += ' hidden';
1650         }
1651         
1652         
1653         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1654             cfg.cls +=' alert alert-' + this.alert;
1655         }
1656         
1657         var body = cfg;
1658         
1659         if (this.panel.length) {
1660             cfg.cls += ' panel panel-' + this.panel;
1661             cfg.cn = [];
1662             if (this.header.length) {
1663                 
1664                 var h = [];
1665                 
1666                 if(this.expandable){
1667                     
1668                     cfg.cls = cfg.cls + ' expandable';
1669                     
1670                     h.push({
1671                         tag: 'i',
1672                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1673                     });
1674                     
1675                 }
1676                 
1677                 h.push(
1678                     {
1679                         tag: 'span',
1680                         cls : 'panel-title',
1681                         html : (this.expandable ? '&nbsp;' : '') + this.header
1682                     },
1683                     {
1684                         tag: 'span',
1685                         cls: 'panel-header-right',
1686                         html: this.rheader
1687                     }
1688                 );
1689                 
1690                 cfg.cn.push({
1691                     cls : 'panel-heading',
1692                     style : this.expandable ? 'cursor: pointer' : '',
1693                     cn : h
1694                 });
1695                 
1696             }
1697             
1698             body = false;
1699             cfg.cn.push({
1700                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1701                 html : this.html
1702             });
1703             
1704             
1705             if (this.footer.length) {
1706                 cfg.cn.push({
1707                     cls : 'panel-footer',
1708                     html : this.footer
1709                     
1710                 });
1711             }
1712             
1713         }
1714         
1715         if (body) {
1716             body.html = this.html || cfg.html;
1717             // prefix with the icons..
1718             if (this.fa) {
1719                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1720             }
1721             if (this.icon) {
1722                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1723             }
1724             
1725             
1726         }
1727         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1728             cfg.cls =  'container';
1729         }
1730         
1731         return cfg;
1732     },
1733     
1734     initEvents: function() 
1735     {
1736         if(this.expandable){
1737             var headerEl = this.headerEl();
1738         
1739             if(headerEl){
1740                 headerEl.on('click', this.onToggleClick, this);
1741             }
1742         }
1743         
1744         if(this.clickable){
1745             this.el.on('click', this.onClick, this);
1746         }
1747         
1748     },
1749     
1750     onToggleClick : function()
1751     {
1752         var headerEl = this.headerEl();
1753         
1754         if(!headerEl){
1755             return;
1756         }
1757         
1758         if(this.expanded){
1759             this.collapse();
1760             return;
1761         }
1762         
1763         this.expand();
1764     },
1765     
1766     expand : function()
1767     {
1768         if(this.fireEvent('expand', this)) {
1769             
1770             this.expanded = true;
1771             
1772             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1773             
1774             this.el.select('.panel-body',true).first().removeClass('hide');
1775             
1776             var toggleEl = this.toggleEl();
1777
1778             if(!toggleEl){
1779                 return;
1780             }
1781
1782             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1783         }
1784         
1785     },
1786     
1787     collapse : function()
1788     {
1789         if(this.fireEvent('collapse', this)) {
1790             
1791             this.expanded = false;
1792             
1793             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1794             this.el.select('.panel-body',true).first().addClass('hide');
1795         
1796             var toggleEl = this.toggleEl();
1797
1798             if(!toggleEl){
1799                 return;
1800             }
1801
1802             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1803         }
1804     },
1805     
1806     toggleEl : function()
1807     {
1808         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1809             return;
1810         }
1811         
1812         return this.el.select('.panel-heading .fa',true).first();
1813     },
1814     
1815     headerEl : function()
1816     {
1817         if(!this.el || !this.panel.length || !this.header.length){
1818             return;
1819         }
1820         
1821         return this.el.select('.panel-heading',true).first()
1822     },
1823     
1824     bodyEl : function()
1825     {
1826         if(!this.el || !this.panel.length){
1827             return;
1828         }
1829         
1830         return this.el.select('.panel-body',true).first()
1831     },
1832     
1833     titleEl : function()
1834     {
1835         if(!this.el || !this.panel.length || !this.header.length){
1836             return;
1837         }
1838         
1839         return this.el.select('.panel-title',true).first();
1840     },
1841     
1842     setTitle : function(v)
1843     {
1844         var titleEl = this.titleEl();
1845         
1846         if(!titleEl){
1847             return;
1848         }
1849         
1850         titleEl.dom.innerHTML = v;
1851     },
1852     
1853     getTitle : function()
1854     {
1855         
1856         var titleEl = this.titleEl();
1857         
1858         if(!titleEl){
1859             return '';
1860         }
1861         
1862         return titleEl.dom.innerHTML;
1863     },
1864     
1865     setRightTitle : function(v)
1866     {
1867         var t = this.el.select('.panel-header-right',true).first();
1868         
1869         if(!t){
1870             return;
1871         }
1872         
1873         t.dom.innerHTML = v;
1874     },
1875     
1876     onClick : function(e)
1877     {
1878         e.preventDefault();
1879         
1880         this.fireEvent('click', this, e);
1881     }
1882 });
1883
1884  /*
1885  *  - LGPL
1886  *
1887  *  This is BS4's Card element.. - similar to our containers probably..
1888  * 
1889  */
1890 /**
1891  * @class Roo.bootstrap.Card
1892  * @extends Roo.bootstrap.Component
1893  * Bootstrap Card class
1894  *
1895  *
1896  * possible... may not be implemented..
1897  * @cfg {String} header_image  src url of image.
1898  * @cfg {String|Object} header
1899  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1900  * 
1901  * @cfg {String} title
1902  * @cfg {String} subtitle
1903  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1904  * @cfg {String} footer
1905  
1906  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1907  * 
1908  * @cfg {String} margin (0|1|2|3|4|5|auto)
1909  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1910  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1911  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1912  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1913  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1914  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1915  *
1916  * @cfg {String} padding (0|1|2|3|4|5)
1917  * @cfg {String} padding_top (0|1|2|3|4|5)
1918  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1919  * @cfg {String} padding_left (0|1|2|3|4|5)
1920  * @cfg {String} padding_right (0|1|2|3|4|5)
1921  * @cfg {String} padding_x (0|1|2|3|4|5)
1922  * @cfg {String} padding_y (0|1|2|3|4|5)
1923  *
1924  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1925  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1926  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1927  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1928  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1929  
1930  * @config {Boolean} dragable  if this card can be dragged.
1931  * @config {String} drag_group  group for drag
1932  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1933  * @config {String} drop_group  group for drag
1934  * 
1935  * @config {Boolean} collapsable can the body be collapsed.
1936  * @config {Boolean} collapsed is the body collapsed when rendered...
1937  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1938  * @config {Boolean} rotated is the body rotated when rendered...
1939  * 
1940  * @constructor
1941  * Create a new Container
1942  * @param {Object} config The config object
1943  */
1944
1945 Roo.bootstrap.Card = function(config){
1946     Roo.bootstrap.Card.superclass.constructor.call(this, config);
1947     
1948     this.addEvents({
1949          // raw events
1950         /**
1951          * @event drop
1952          * When a element a card is dropped
1953          * @param {Roo.bootstrap.Element} this
1954          * @param {Roo.Element} n the node being dropped?
1955          * @param {Object} dd Drag and drop data
1956          * @param {Roo.EventObject} e
1957          * @param {Roo.EventObject} data  the data passed via getDragData
1958          */
1959         'drop' : true,
1960          /**
1961          * @event rotate
1962          * When a element a card is rotate
1963          * @param {Roo.bootstrap.Element} this
1964          * @param {Roo.Element} n the node being dropped?
1965          * @param {Boolean} rotate status
1966          */
1967         'rotate' : true
1968         
1969     });
1970 };
1971
1972
1973 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
1974     
1975     
1976     weight : '',
1977     
1978     margin: '', /// may be better in component?
1979     margin_top: '', 
1980     margin_bottom: '', 
1981     margin_left: '',
1982     margin_right: '',
1983     margin_x: '',
1984     margin_y: '',
1985     
1986     padding : '',
1987     padding_top: '', 
1988     padding_bottom: '', 
1989     padding_left: '',
1990     padding_right: '',
1991     padding_x: '',
1992     padding_y: '',
1993     
1994     display: '', 
1995     display_xs: '', 
1996     display_sm: '', 
1997     display_lg: '',
1998     display_xl: '',
1999  
2000     header_image  : '',
2001     header : '',
2002     header_size : 0,
2003     title : '',
2004     subtitle : '',
2005     html : '',
2006     footer: '',
2007
2008     collapsable : false,
2009     collapsed : false,
2010     rotateable : false,
2011     rotated : false,
2012     
2013     dragable : false,
2014     drag_group : false,
2015     dropable : false,
2016     drop_group : false,
2017     childContainer : false,
2018     dropEl : false, /// the dom placeholde element that indicates drop location.
2019     
2020     layoutCls : function()
2021     {
2022         var cls = '';
2023         var t = this;
2024         Roo.log(this.margin_bottom.length);
2025         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2026             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2027             
2028             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2029                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2030             }
2031             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2032                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2033             }
2034         });
2035         
2036         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2037             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2038                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
2039             }
2040         });
2041         
2042         // more generic support?
2043         if (this.hidden) {
2044             cls += ' d-none';
2045         }
2046         
2047         return cls;
2048     },
2049  
2050        // Roo.log("Call onRender: " + this.xtype);
2051         /*  We are looking at something like this.
2052 <div class="card">
2053     <img src="..." class="card-img-top" alt="...">
2054     <div class="card-body">
2055         <h5 class="card-title">Card title</h5>
2056          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2057
2058         >> this bit is really the body...
2059         <div> << we will ad dthis in hopefully it will not break shit.
2060         
2061         ** card text does not actually have any styling...
2062         
2063             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2064         
2065         </div> <<
2066           <a href="#" class="card-link">Card link</a>
2067           
2068     </div>
2069     <div class="card-footer">
2070         <small class="text-muted">Last updated 3 mins ago</small>
2071     </div>
2072 </div>
2073          */
2074     getAutoCreate : function(){
2075         
2076         var cfg = {
2077             tag : 'div',
2078             cls : 'card',
2079             cn : [ ]
2080         };
2081         
2082         if (this.weight.length && this.weight != 'light') {
2083             cfg.cls += ' text-white';
2084         } else {
2085             cfg.cls += ' text-dark'; // need as it's nested..
2086         }
2087         if (this.weight.length) {
2088             cfg.cls += ' bg-' + this.weight;
2089         }
2090         
2091         cfg.cls += this.layoutCls(); 
2092         
2093         var hdr = false;
2094         var hdr_ctr = false;
2095         if (this.header.length) {
2096             hdr = {
2097                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2098                 cls : 'card-header',
2099                 cn : []
2100             };
2101             cfg.cn.push(hdr);
2102             hdr_ctr = hdr;
2103         } else {
2104             hdr = {
2105                 tag : 'div',
2106                 cls : 'card-header d-none',
2107                 cn : []
2108             };
2109             cfg.cn.push(hdr);
2110             hdr_ctr = hdr;
2111         }
2112         if (this.collapsable) {
2113             hdr_ctr = {
2114             tag : 'a',
2115             cls : 'd-block user-select-none',
2116             cn: [
2117                     {
2118                         tag: 'i',
2119                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2120                     }
2121                    
2122                 ]
2123             };
2124             hdr.cn.push(hdr_ctr);
2125         }
2126         
2127         hdr_ctr.cn.push(        {
2128             tag: 'span',
2129             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2130             html : this.header
2131         });
2132         
2133         
2134         if (this.header_image.length) {
2135             cfg.cn.push({
2136                 tag : 'img',
2137                 cls : 'card-img-top',
2138                 src: this.header_image // escape?
2139             });
2140         } else {
2141             cfg.cn.push({
2142                     tag : 'div',
2143                     cls : 'card-img-top d-none' 
2144                 });
2145         }
2146             
2147         var body = {
2148             tag : 'div',
2149             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2150             cn : []
2151         };
2152         var obody = body;
2153         if (this.collapsable || this.rotateable) {
2154             obody = {
2155                 tag: 'div',
2156                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2157                 cn : [  body ]
2158             };
2159         }
2160         
2161         cfg.cn.push(obody);
2162         
2163         if (this.title.length) {
2164             body.cn.push({
2165                 tag : 'div',
2166                 cls : 'card-title',
2167                 src: this.title // escape?
2168             });
2169         }  
2170         
2171         if (this.subtitle.length) {
2172             body.cn.push({
2173                 tag : 'div',
2174                 cls : 'card-title',
2175                 src: this.subtitle // escape?
2176             });
2177         }
2178         
2179         body.cn.push({
2180             tag : 'div',
2181             cls : 'roo-card-body-ctr'
2182         });
2183         
2184         if (this.html.length) {
2185             body.cn.push({
2186                 tag: 'div',
2187                 html : this.html
2188             });
2189         }
2190         // fixme ? handle objects?
2191         
2192         if (this.footer.length) {
2193            
2194             cfg.cn.push({
2195                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2196                 html : this.footer
2197             });
2198             
2199         } else {
2200             cfg.cn.push({cls : 'card-footer d-none'});
2201         }
2202         
2203         // footer...
2204         
2205         return cfg;
2206     },
2207     
2208     
2209     getCardHeader : function()
2210     {
2211         var  ret = this.el.select('.card-header',true).first();
2212         if (ret.hasClass('d-none')) {
2213             ret.removeClass('d-none');
2214         }
2215         
2216         return ret;
2217     },
2218     getCardFooter : function()
2219     {
2220         var  ret = this.el.select('.card-footer',true).first();
2221         if (ret.hasClass('d-none')) {
2222             ret.removeClass('d-none');
2223         }
2224         
2225         return ret;
2226     },
2227     getCardImageTop : function()
2228     {
2229         var  ret = this.el.select('.card-img-top',true).first();
2230         if (ret.hasClass('d-none')) {
2231             ret.removeClass('d-none');
2232         }
2233             
2234         return ret;
2235     },
2236     
2237     getChildContainer : function()
2238     {
2239         
2240         if(!this.el){
2241             return false;
2242         }
2243         return this.el.select('.roo-card-body-ctr',true).first();    
2244     },
2245     
2246     initEvents: function() 
2247     {
2248         
2249         this.bodyEl = this.getChildContainer();
2250         if(this.dragable){
2251             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2252                     containerScroll: true,
2253                     ddGroup: this.drag_group || 'default_card_drag_group'
2254             });
2255             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2256         }
2257         if (this.dropable) {
2258             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2259                 containerScroll: true,
2260                 ddGroup: this.drop_group || 'default_card_drag_group'
2261             });
2262             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2263             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2264             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2265             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2266             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2267         }
2268         
2269         if (this.collapsable) {
2270             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2271         }
2272         if (this.rotateable) {
2273             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2274         }
2275         this.collapsableEl = this.el.select('.roo-collapsable').first();
2276          
2277         this.footerEl = this.el.select('.card-footer').first();
2278         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2279         this.headerEl = this.el.select('.roo-card-header-ctr').first();
2280         
2281         if (this.rotated) {
2282             this.el.addClass('roo-card-rotated');
2283             this.fireEvent('rotate', this, true);
2284         }
2285         
2286     },
2287     getDragData : function(e)
2288     {
2289         var target = this.getEl();
2290         if (target) {
2291             //this.handleSelection(e);
2292             
2293             var dragData = {
2294                 source: this,
2295                 copy: false,
2296                 nodes: this.getEl(),
2297                 records: []
2298             };
2299             
2300             
2301             dragData.ddel = target.dom ;    // the div element
2302             Roo.log(target.getWidth( ));
2303             dragData.ddel.style.width = target.getWidth() + 'px';
2304             
2305             return dragData;
2306         }
2307         return false;
2308     },
2309     /**
2310     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2311     *    whole Element becomes the target, and this causes the drop gesture to append.
2312     */
2313     getTargetFromEvent : function(e, dragged_card_el)
2314     {
2315         var target = e.getTarget();
2316         while ((target !== null) && (target.parentNode != this.bodyEl.dom)) {
2317             target = target.parentNode;
2318         }
2319         
2320         var ret = {
2321             position: '',
2322             cards : [],
2323             card_n : -1,
2324             items_n : -1,
2325             card : false 
2326         };
2327         
2328         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2329         // see if target is one of the 'cards'...
2330         
2331         
2332         //Roo.log(this.items.length);
2333         var pos = false;
2334         
2335         var last_card_n = 0;
2336         var cards_len  = 0;
2337         for (var i = 0;i< this.items.length;i++) {
2338             
2339             if (!this.items[i].el.hasClass('card')) {
2340                  continue;
2341             }
2342             pos = this.getDropPoint(e, this.items[i].el.dom);
2343             
2344             cards_len = ret.cards.length;
2345             //Roo.log(this.items[i].el.dom.id);
2346             ret.cards.push(this.items[i]);
2347             last_card_n  = i;
2348             if (ret.card_n < 0 && pos == 'above') {
2349                 ret.position = cards_len > 0 ? 'below' : pos;
2350                 ret.items_n = i > 0 ? i - 1 : 0;
2351                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2352                 ret.card = ret.cards[ret.card_n];
2353             }
2354         }
2355         if (!ret.cards.length) {
2356             ret.card = true;
2357             ret.position = 'below';
2358             ret.items_n;
2359             return ret;
2360         }
2361         // could not find a card.. stick it at the end..
2362         if (ret.card_n < 0) {
2363             ret.card_n = last_card_n;
2364             ret.card = ret.cards[last_card_n];
2365             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2366             ret.position = 'below';
2367         }
2368         
2369         if (this.items[ret.items_n].el == dragged_card_el) {
2370             return false;
2371         }
2372         
2373         if (ret.position == 'below') {
2374             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2375             
2376             if (card_after  && card_after.el == dragged_card_el) {
2377                 return false;
2378             }
2379             return ret;
2380         }
2381         
2382         // its's after ..
2383         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2384         
2385         if (card_before  && card_before.el == dragged_card_el) {
2386             return false;
2387         }
2388         
2389         return ret;
2390     },
2391     
2392     onNodeEnter : function(n, dd, e, data){
2393         return false;
2394     },
2395     onNodeOver : function(n, dd, e, data)
2396     {
2397        
2398         var target_info = this.getTargetFromEvent(e,data.source.el);
2399         if (target_info === false) {
2400             this.dropPlaceHolder('hide');
2401             return false;
2402         }
2403         Roo.log(['getTargetFromEvent', target_info ]);
2404         
2405          
2406         this.dropPlaceHolder('show', target_info,data);
2407         
2408         return false; 
2409     },
2410     onNodeOut : function(n, dd, e, data){
2411         this.dropPlaceHolder('hide');
2412      
2413     },
2414     onNodeDrop : function(n, dd, e, data)
2415     {
2416         
2417         // call drop - return false if
2418         
2419         // this could actually fail - if the Network drops..
2420         // we will ignore this at present..- client should probably reload
2421         // the whole set of cards if stuff like that fails.
2422         
2423         
2424         var info = this.getTargetFromEvent(e,data.source.el);
2425         if (info === false) {
2426             return false;
2427         }
2428         
2429         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
2430             return false;
2431         }
2432          
2433         this.dropPlaceHolder('hide');
2434         
2435         // do the dom manipulation first..
2436         var dom = data.source.el.dom;
2437         dom.parentNode.removeChild(dom);
2438         
2439         
2440         if (info.card !== true) {
2441             var cardel = info.card.el.dom;
2442             
2443             if (info.position == 'above') {
2444                 cardel.parentNode.insertBefore(dom, cardel);
2445             } else if (cardel.nextSibling) {
2446                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2447             } else {
2448                 cardel.parentNode.append(dom);
2449             }
2450         } else {
2451             // card container???
2452             this.bodyEl.dom.append(dom);
2453         }
2454         
2455         //FIXME HANDLE card = true 
2456         
2457         // add this to the correct place in items.
2458         
2459         
2460         
2461         // remove Card from items.
2462         
2463         var old_parent = data.source.parent();
2464         
2465         old_parent.items = old_parent.items.filter(function(e) { return e != data.source });
2466         
2467         if (this.items.length) {
2468             var nitems = [];
2469             //Roo.log([info.items_n, info.position, this.items.length]);
2470             for (var i =0; i < this.items.length; i++) {
2471                 if (i == info.items_n && info.position == 'above') {
2472                     nitems.push(data.source);
2473                 }
2474                 nitems.push(this.items[i]);
2475                 if (i == info.items_n && info.position == 'below') {
2476                     nitems.push(data.source);
2477                 }
2478             }
2479             this.items = nitems;
2480             Roo.log(this.items);
2481         } else {
2482             this.items.push(data.source);
2483         }
2484         
2485         data.source.parentId = this.id;
2486         
2487         return true;
2488     },
2489     
2490     /**    Decide whether to drop above or below a View node. */
2491     getDropPoint : function(e, n, dd)
2492     {
2493         if (dd) {
2494              return false;
2495         }
2496         if (n == this.bodyEl.dom) {
2497             return "above";
2498         }
2499         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2500         var c = t + (b - t) / 2;
2501         var y = Roo.lib.Event.getPageY(e);
2502         if(y <= c) {
2503             return "above";
2504         }else{
2505             return "below";
2506         }
2507     },
2508     onToggleCollapse : function(e)
2509         {
2510         if (this.collapsed) {
2511             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2512             this.collapsableEl.addClass('show');
2513             this.collapsed = false;
2514             return;
2515         }
2516         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2517         this.collapsableEl.removeClass('show');
2518         this.collapsed = true;
2519         
2520     
2521     },
2522     
2523     onToggleRotate : function(e)
2524     {
2525         this.collapsableEl.removeClass('show');
2526         this.footerEl.removeClass('d-none');
2527         this.el.removeClass('roo-card-rotated');
2528         this.el.removeClass('d-none');
2529         if (this.rotated) {
2530             
2531             this.collapsableEl.addClass('show');
2532             this.rotated = false;
2533             this.fireEvent('rotate', this, this.rotated);
2534             return;
2535         }
2536         this.el.addClass('roo-card-rotated');
2537         this.footerEl.addClass('d-none');
2538         this.el.select('.roo-collapsable').removeClass('show');
2539         
2540         this.rotated = true;
2541         this.fireEvent('rotate', this, this.rotated);
2542     
2543     },
2544     
2545     dropPlaceHolder: function (action, info, data)
2546     {
2547         if (this.dropEl === false) {
2548             this.dropEl = Roo.DomHelper.append(this.bodyEl, {
2549             cls : 'd-none'
2550             },true);
2551         }
2552         this.dropEl.removeClass(['d-none', 'd-block']);        
2553         if (action == 'hide') {
2554             
2555             this.dropEl.addClass('d-none');
2556             return;
2557         }
2558         // FIXME - info.card == true!!!
2559         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2560         
2561         if (info.card !== true) {
2562             var cardel = info.card.el.dom;
2563             
2564             if (info.position == 'above') {
2565                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2566             } else if (cardel.nextSibling) {
2567                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2568             } else {
2569                 cardel.parentNode.append(this.dropEl.dom);
2570             }
2571         } else {
2572             // card container???
2573             this.bodyEl.dom.append(this.dropEl.dom);
2574         }
2575         
2576         this.dropEl.addClass('d-block roo-card-dropzone');
2577         
2578         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2579         
2580         
2581     
2582     
2583     
2584     },
2585     setHeaderText: function(html)
2586     {
2587         this.headerEl.dom.innerHTML = html;
2588     }
2589
2590     
2591 });
2592
2593 /*
2594  * - LGPL
2595  *
2596  * Card header - holder for the card header elements.
2597  * 
2598  */
2599
2600 /**
2601  * @class Roo.bootstrap.CardHeader
2602  * @extends Roo.bootstrap.Element
2603  * Bootstrap CardHeader class
2604  * @constructor
2605  * Create a new Card Header - that you can embed children into
2606  * @param {Object} config The config object
2607  */
2608
2609 Roo.bootstrap.CardHeader = function(config){
2610     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2611 };
2612
2613 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2614     
2615     
2616     container_method : 'getCardHeader' 
2617     
2618      
2619     
2620     
2621    
2622 });
2623
2624  
2625
2626  /*
2627  * - LGPL
2628  *
2629  * Card footer - holder for the card footer elements.
2630  * 
2631  */
2632
2633 /**
2634  * @class Roo.bootstrap.CardFooter
2635  * @extends Roo.bootstrap.Element
2636  * Bootstrap CardFooter class
2637  * @constructor
2638  * Create a new Card Footer - that you can embed children into
2639  * @param {Object} config The config object
2640  */
2641
2642 Roo.bootstrap.CardFooter = function(config){
2643     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2644 };
2645
2646 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2647     
2648     
2649     container_method : 'getCardFooter' 
2650     
2651      
2652     
2653     
2654    
2655 });
2656
2657  
2658
2659  /*
2660  * - LGPL
2661  *
2662  * Card header - holder for the card header elements.
2663  * 
2664  */
2665
2666 /**
2667  * @class Roo.bootstrap.CardImageTop
2668  * @extends Roo.bootstrap.Element
2669  * Bootstrap CardImageTop class
2670  * @constructor
2671  * Create a new Card Image Top container
2672  * @param {Object} config The config object
2673  */
2674
2675 Roo.bootstrap.CardImageTop = function(config){
2676     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2677 };
2678
2679 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2680     
2681    
2682     container_method : 'getCardImageTop' 
2683     
2684      
2685     
2686    
2687 });
2688
2689  
2690
2691  /*
2692  * - LGPL
2693  *
2694  * image
2695  * 
2696  */
2697
2698
2699 /**
2700  * @class Roo.bootstrap.Img
2701  * @extends Roo.bootstrap.Component
2702  * Bootstrap Img class
2703  * @cfg {Boolean} imgResponsive false | true
2704  * @cfg {String} border rounded | circle | thumbnail
2705  * @cfg {String} src image source
2706  * @cfg {String} alt image alternative text
2707  * @cfg {String} href a tag href
2708  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2709  * @cfg {String} xsUrl xs image source
2710  * @cfg {String} smUrl sm image source
2711  * @cfg {String} mdUrl md image source
2712  * @cfg {String} lgUrl lg image source
2713  * 
2714  * @constructor
2715  * Create a new Input
2716  * @param {Object} config The config object
2717  */
2718
2719 Roo.bootstrap.Img = function(config){
2720     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2721     
2722     this.addEvents({
2723         // img events
2724         /**
2725          * @event click
2726          * The img click event for the img.
2727          * @param {Roo.EventObject} e
2728          */
2729         "click" : true
2730     });
2731 };
2732
2733 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2734     
2735     imgResponsive: true,
2736     border: '',
2737     src: 'about:blank',
2738     href: false,
2739     target: false,
2740     xsUrl: '',
2741     smUrl: '',
2742     mdUrl: '',
2743     lgUrl: '',
2744
2745     getAutoCreate : function()
2746     {   
2747         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2748             return this.createSingleImg();
2749         }
2750         
2751         var cfg = {
2752             tag: 'div',
2753             cls: 'roo-image-responsive-group',
2754             cn: []
2755         };
2756         var _this = this;
2757         
2758         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2759             
2760             if(!_this[size + 'Url']){
2761                 return;
2762             }
2763             
2764             var img = {
2765                 tag: 'img',
2766                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2767                 html: _this.html || cfg.html,
2768                 src: _this[size + 'Url']
2769             };
2770             
2771             img.cls += ' roo-image-responsive-' + size;
2772             
2773             var s = ['xs', 'sm', 'md', 'lg'];
2774             
2775             s.splice(s.indexOf(size), 1);
2776             
2777             Roo.each(s, function(ss){
2778                 img.cls += ' hidden-' + ss;
2779             });
2780             
2781             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2782                 cfg.cls += ' img-' + _this.border;
2783             }
2784             
2785             if(_this.alt){
2786                 cfg.alt = _this.alt;
2787             }
2788             
2789             if(_this.href){
2790                 var a = {
2791                     tag: 'a',
2792                     href: _this.href,
2793                     cn: [
2794                         img
2795                     ]
2796                 };
2797
2798                 if(this.target){
2799                     a.target = _this.target;
2800                 }
2801             }
2802             
2803             cfg.cn.push((_this.href) ? a : img);
2804             
2805         });
2806         
2807         return cfg;
2808     },
2809     
2810     createSingleImg : function()
2811     {
2812         var cfg = {
2813             tag: 'img',
2814             cls: (this.imgResponsive) ? 'img-responsive' : '',
2815             html : null,
2816             src : 'about:blank'  // just incase src get's set to undefined?!?
2817         };
2818         
2819         cfg.html = this.html || cfg.html;
2820         
2821         cfg.src = this.src || cfg.src;
2822         
2823         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2824             cfg.cls += ' img-' + this.border;
2825         }
2826         
2827         if(this.alt){
2828             cfg.alt = this.alt;
2829         }
2830         
2831         if(this.href){
2832             var a = {
2833                 tag: 'a',
2834                 href: this.href,
2835                 cn: [
2836                     cfg
2837                 ]
2838             };
2839             
2840             if(this.target){
2841                 a.target = this.target;
2842             }
2843             
2844         }
2845         
2846         return (this.href) ? a : cfg;
2847     },
2848     
2849     initEvents: function() 
2850     {
2851         if(!this.href){
2852             this.el.on('click', this.onClick, this);
2853         }
2854         
2855     },
2856     
2857     onClick : function(e)
2858     {
2859         Roo.log('img onclick');
2860         this.fireEvent('click', this, e);
2861     },
2862     /**
2863      * Sets the url of the image - used to update it
2864      * @param {String} url the url of the image
2865      */
2866     
2867     setSrc : function(url)
2868     {
2869         this.src =  url;
2870         
2871         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2872             this.el.dom.src =  url;
2873             return;
2874         }
2875         
2876         this.el.select('img', true).first().dom.src =  url;
2877     }
2878     
2879     
2880    
2881 });
2882
2883  /*
2884  * - LGPL
2885  *
2886  * image
2887  * 
2888  */
2889
2890
2891 /**
2892  * @class Roo.bootstrap.Link
2893  * @extends Roo.bootstrap.Component
2894  * Bootstrap Link Class
2895  * @cfg {String} alt image alternative text
2896  * @cfg {String} href a tag href
2897  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2898  * @cfg {String} html the content of the link.
2899  * @cfg {String} anchor name for the anchor link
2900  * @cfg {String} fa - favicon
2901
2902  * @cfg {Boolean} preventDefault (true | false) default false
2903
2904  * 
2905  * @constructor
2906  * Create a new Input
2907  * @param {Object} config The config object
2908  */
2909
2910 Roo.bootstrap.Link = function(config){
2911     Roo.bootstrap.Link.superclass.constructor.call(this, config);
2912     
2913     this.addEvents({
2914         // img events
2915         /**
2916          * @event click
2917          * The img click event for the img.
2918          * @param {Roo.EventObject} e
2919          */
2920         "click" : true
2921     });
2922 };
2923
2924 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
2925     
2926     href: false,
2927     target: false,
2928     preventDefault: false,
2929     anchor : false,
2930     alt : false,
2931     fa: false,
2932
2933
2934     getAutoCreate : function()
2935     {
2936         var html = this.html || '';
2937         
2938         if (this.fa !== false) {
2939             html = '<i class="fa fa-' + this.fa + '"></i>';
2940         }
2941         var cfg = {
2942             tag: 'a'
2943         };
2944         // anchor's do not require html/href...
2945         if (this.anchor === false) {
2946             cfg.html = html;
2947             cfg.href = this.href || '#';
2948         } else {
2949             cfg.name = this.anchor;
2950             if (this.html !== false || this.fa !== false) {
2951                 cfg.html = html;
2952             }
2953             if (this.href !== false) {
2954                 cfg.href = this.href;
2955             }
2956         }
2957         
2958         if(this.alt !== false){
2959             cfg.alt = this.alt;
2960         }
2961         
2962         
2963         if(this.target !== false) {
2964             cfg.target = this.target;
2965         }
2966         
2967         return cfg;
2968     },
2969     
2970     initEvents: function() {
2971         
2972         if(!this.href || this.preventDefault){
2973             this.el.on('click', this.onClick, this);
2974         }
2975     },
2976     
2977     onClick : function(e)
2978     {
2979         if(this.preventDefault){
2980             e.preventDefault();
2981         }
2982         //Roo.log('img onclick');
2983         this.fireEvent('click', this, e);
2984     }
2985    
2986 });
2987
2988  /*
2989  * - LGPL
2990  *
2991  * header
2992  * 
2993  */
2994
2995 /**
2996  * @class Roo.bootstrap.Header
2997  * @extends Roo.bootstrap.Component
2998  * Bootstrap Header class
2999  * @cfg {String} html content of header
3000  * @cfg {Number} level (1|2|3|4|5|6) default 1
3001  * 
3002  * @constructor
3003  * Create a new Header
3004  * @param {Object} config The config object
3005  */
3006
3007
3008 Roo.bootstrap.Header  = function(config){
3009     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3010 };
3011
3012 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3013     
3014     //href : false,
3015     html : false,
3016     level : 1,
3017     
3018     
3019     
3020     getAutoCreate : function(){
3021         
3022         
3023         
3024         var cfg = {
3025             tag: 'h' + (1 *this.level),
3026             html: this.html || ''
3027         } ;
3028         
3029         return cfg;
3030     }
3031    
3032 });
3033
3034  
3035
3036  /*
3037  * Based on:
3038  * Ext JS Library 1.1.1
3039  * Copyright(c) 2006-2007, Ext JS, LLC.
3040  *
3041  * Originally Released Under LGPL - original licence link has changed is not relivant.
3042  *
3043  * Fork - LGPL
3044  * <script type="text/javascript">
3045  */
3046  
3047 /**
3048  * @class Roo.bootstrap.MenuMgr
3049  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3050  * @singleton
3051  */
3052 Roo.bootstrap.MenuMgr = function(){
3053    var menus, active, groups = {}, attached = false, lastShow = new Date();
3054
3055    // private - called when first menu is created
3056    function init(){
3057        menus = {};
3058        active = new Roo.util.MixedCollection();
3059        Roo.get(document).addKeyListener(27, function(){
3060            if(active.length > 0){
3061                hideAll();
3062            }
3063        });
3064    }
3065
3066    // private
3067    function hideAll(){
3068        if(active && active.length > 0){
3069            var c = active.clone();
3070            c.each(function(m){
3071                m.hide();
3072            });
3073        }
3074    }
3075
3076    // private
3077    function onHide(m){
3078        active.remove(m);
3079        if(active.length < 1){
3080            Roo.get(document).un("mouseup", onMouseDown);
3081             
3082            attached = false;
3083        }
3084    }
3085
3086    // private
3087    function onShow(m){
3088        var last = active.last();
3089        lastShow = new Date();
3090        active.add(m);
3091        if(!attached){
3092           Roo.get(document).on("mouseup", onMouseDown);
3093            
3094            attached = true;
3095        }
3096        if(m.parentMenu){
3097           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3098           m.parentMenu.activeChild = m;
3099        }else if(last && last.isVisible()){
3100           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3101        }
3102    }
3103
3104    // private
3105    function onBeforeHide(m){
3106        if(m.activeChild){
3107            m.activeChild.hide();
3108        }
3109        if(m.autoHideTimer){
3110            clearTimeout(m.autoHideTimer);
3111            delete m.autoHideTimer;
3112        }
3113    }
3114
3115    // private
3116    function onBeforeShow(m){
3117        var pm = m.parentMenu;
3118        if(!pm && !m.allowOtherMenus){
3119            hideAll();
3120        }else if(pm && pm.activeChild && active != m){
3121            pm.activeChild.hide();
3122        }
3123    }
3124
3125    // private this should really trigger on mouseup..
3126    function onMouseDown(e){
3127         Roo.log("on Mouse Up");
3128         
3129         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3130             Roo.log("MenuManager hideAll");
3131             hideAll();
3132             e.stopEvent();
3133         }
3134         
3135         
3136    }
3137
3138    // private
3139    function onBeforeCheck(mi, state){
3140        if(state){
3141            var g = groups[mi.group];
3142            for(var i = 0, l = g.length; i < l; i++){
3143                if(g[i] != mi){
3144                    g[i].setChecked(false);
3145                }
3146            }
3147        }
3148    }
3149
3150    return {
3151
3152        /**
3153         * Hides all menus that are currently visible
3154         */
3155        hideAll : function(){
3156             hideAll();  
3157        },
3158
3159        // private
3160        register : function(menu){
3161            if(!menus){
3162                init();
3163            }
3164            menus[menu.id] = menu;
3165            menu.on("beforehide", onBeforeHide);
3166            menu.on("hide", onHide);
3167            menu.on("beforeshow", onBeforeShow);
3168            menu.on("show", onShow);
3169            var g = menu.group;
3170            if(g && menu.events["checkchange"]){
3171                if(!groups[g]){
3172                    groups[g] = [];
3173                }
3174                groups[g].push(menu);
3175                menu.on("checkchange", onCheck);
3176            }
3177        },
3178
3179         /**
3180          * Returns a {@link Roo.menu.Menu} object
3181          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3182          * be used to generate and return a new Menu instance.
3183          */
3184        get : function(menu){
3185            if(typeof menu == "string"){ // menu id
3186                return menus[menu];
3187            }else if(menu.events){  // menu instance
3188                return menu;
3189            }
3190            /*else if(typeof menu.length == 'number'){ // array of menu items?
3191                return new Roo.bootstrap.Menu({items:menu});
3192            }else{ // otherwise, must be a config
3193                return new Roo.bootstrap.Menu(menu);
3194            }
3195            */
3196            return false;
3197        },
3198
3199        // private
3200        unregister : function(menu){
3201            delete menus[menu.id];
3202            menu.un("beforehide", onBeforeHide);
3203            menu.un("hide", onHide);
3204            menu.un("beforeshow", onBeforeShow);
3205            menu.un("show", onShow);
3206            var g = menu.group;
3207            if(g && menu.events["checkchange"]){
3208                groups[g].remove(menu);
3209                menu.un("checkchange", onCheck);
3210            }
3211        },
3212
3213        // private
3214        registerCheckable : function(menuItem){
3215            var g = menuItem.group;
3216            if(g){
3217                if(!groups[g]){
3218                    groups[g] = [];
3219                }
3220                groups[g].push(menuItem);
3221                menuItem.on("beforecheckchange", onBeforeCheck);
3222            }
3223        },
3224
3225        // private
3226        unregisterCheckable : function(menuItem){
3227            var g = menuItem.group;
3228            if(g){
3229                groups[g].remove(menuItem);
3230                menuItem.un("beforecheckchange", onBeforeCheck);
3231            }
3232        }
3233    };
3234 }();/*
3235  * - LGPL
3236  *
3237  * menu
3238  * 
3239  */
3240
3241 /**
3242  * @class Roo.bootstrap.Menu
3243  * @extends Roo.bootstrap.Component
3244  * Bootstrap Menu class - container for MenuItems
3245  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3246  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3247  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3248  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3249  * 
3250  * @constructor
3251  * Create a new Menu
3252  * @param {Object} config The config object
3253  */
3254
3255
3256 Roo.bootstrap.Menu = function(config){
3257     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3258     if (this.registerMenu && this.type != 'treeview')  {
3259         Roo.bootstrap.MenuMgr.register(this);
3260     }
3261     
3262     
3263     this.addEvents({
3264         /**
3265          * @event beforeshow
3266          * Fires before this menu is displayed (return false to block)
3267          * @param {Roo.menu.Menu} this
3268          */
3269         beforeshow : true,
3270         /**
3271          * @event beforehide
3272          * Fires before this menu is hidden (return false to block)
3273          * @param {Roo.menu.Menu} this
3274          */
3275         beforehide : true,
3276         /**
3277          * @event show
3278          * Fires after this menu is displayed
3279          * @param {Roo.menu.Menu} this
3280          */
3281         show : true,
3282         /**
3283          * @event hide
3284          * Fires after this menu is hidden
3285          * @param {Roo.menu.Menu} this
3286          */
3287         hide : true,
3288         /**
3289          * @event click
3290          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3291          * @param {Roo.menu.Menu} this
3292          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3293          * @param {Roo.EventObject} e
3294          */
3295         click : true,
3296         /**
3297          * @event mouseover
3298          * Fires when the mouse is hovering over this menu
3299          * @param {Roo.menu.Menu} this
3300          * @param {Roo.EventObject} e
3301          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3302          */
3303         mouseover : true,
3304         /**
3305          * @event mouseout
3306          * Fires when the mouse exits this menu
3307          * @param {Roo.menu.Menu} this
3308          * @param {Roo.EventObject} e
3309          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3310          */
3311         mouseout : true,
3312         /**
3313          * @event itemclick
3314          * Fires when a menu item contained in this menu is clicked
3315          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3316          * @param {Roo.EventObject} e
3317          */
3318         itemclick: true
3319     });
3320     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3321 };
3322
3323 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3324     
3325    /// html : false,
3326     //align : '',
3327     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3328     type: false,
3329     /**
3330      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3331      */
3332     registerMenu : true,
3333     
3334     menuItems :false, // stores the menu items..
3335     
3336     hidden:true,
3337         
3338     parentMenu : false,
3339     
3340     stopEvent : true,
3341     
3342     isLink : false,
3343     
3344     getChildContainer : function() {
3345         return this.el;  
3346     },
3347     
3348     getAutoCreate : function(){
3349          
3350         //if (['right'].indexOf(this.align)!==-1) {
3351         //    cfg.cn[1].cls += ' pull-right'
3352         //}
3353         
3354         
3355         var cfg = {
3356             tag : 'ul',
3357             cls : 'dropdown-menu' ,
3358             style : 'z-index:1000'
3359             
3360         };
3361         
3362         if (this.type === 'submenu') {
3363             cfg.cls = 'submenu active';
3364         }
3365         if (this.type === 'treeview') {
3366             cfg.cls = 'treeview-menu';
3367         }
3368         
3369         return cfg;
3370     },
3371     initEvents : function() {
3372         
3373        // Roo.log("ADD event");
3374        // Roo.log(this.triggerEl.dom);
3375         
3376         this.triggerEl.on('click', this.onTriggerClick, this);
3377         
3378         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3379         
3380         
3381         if (this.triggerEl.hasClass('nav-item')) {
3382             // dropdown toggle on the 'a' in BS4?
3383             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3384         } else {
3385             this.triggerEl.addClass('dropdown-toggle');
3386         }
3387         if (Roo.isTouch) {
3388             this.el.on('touchstart'  , this.onTouch, this);
3389         }
3390         this.el.on('click' , this.onClick, this);
3391
3392         this.el.on("mouseover", this.onMouseOver, this);
3393         this.el.on("mouseout", this.onMouseOut, this);
3394         
3395     },
3396     
3397     findTargetItem : function(e)
3398     {
3399         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3400         if(!t){
3401             return false;
3402         }
3403         //Roo.log(t);         Roo.log(t.id);
3404         if(t && t.id){
3405             //Roo.log(this.menuitems);
3406             return this.menuitems.get(t.id);
3407             
3408             //return this.items.get(t.menuItemId);
3409         }
3410         
3411         return false;
3412     },
3413     
3414     onTouch : function(e) 
3415     {
3416         Roo.log("menu.onTouch");
3417         //e.stopEvent(); this make the user popdown broken
3418         this.onClick(e);
3419     },
3420     
3421     onClick : function(e)
3422     {
3423         Roo.log("menu.onClick");
3424         
3425         var t = this.findTargetItem(e);
3426         if(!t || t.isContainer){
3427             return;
3428         }
3429         Roo.log(e);
3430         /*
3431         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3432             if(t == this.activeItem && t.shouldDeactivate(e)){
3433                 this.activeItem.deactivate();
3434                 delete this.activeItem;
3435                 return;
3436             }
3437             if(t.canActivate){
3438                 this.setActiveItem(t, true);
3439             }
3440             return;
3441             
3442             
3443         }
3444         */
3445        
3446         Roo.log('pass click event');
3447         
3448         t.onClick(e);
3449         
3450         this.fireEvent("click", this, t, e);
3451         
3452         var _this = this;
3453         
3454         if(!t.href.length || t.href == '#'){
3455             (function() { _this.hide(); }).defer(100);
3456         }
3457         
3458     },
3459     
3460     onMouseOver : function(e){
3461         var t  = this.findTargetItem(e);
3462         //Roo.log(t);
3463         //if(t){
3464         //    if(t.canActivate && !t.disabled){
3465         //        this.setActiveItem(t, true);
3466         //    }
3467         //}
3468         
3469         this.fireEvent("mouseover", this, e, t);
3470     },
3471     isVisible : function(){
3472         return !this.hidden;
3473     },
3474     onMouseOut : function(e){
3475         var t  = this.findTargetItem(e);
3476         
3477         //if(t ){
3478         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3479         //        this.activeItem.deactivate();
3480         //        delete this.activeItem;
3481         //    }
3482         //}
3483         this.fireEvent("mouseout", this, e, t);
3484     },
3485     
3486     
3487     /**
3488      * Displays this menu relative to another element
3489      * @param {String/HTMLElement/Roo.Element} element The element to align to
3490      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3491      * the element (defaults to this.defaultAlign)
3492      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3493      */
3494     show : function(el, pos, parentMenu)
3495     {
3496         if (false === this.fireEvent("beforeshow", this)) {
3497             Roo.log("show canceled");
3498             return;
3499         }
3500         this.parentMenu = parentMenu;
3501         if(!this.el){
3502             this.render();
3503         }
3504         
3505         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3506     },
3507      /**
3508      * Displays this menu at a specific xy position
3509      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3510      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3511      */
3512     showAt : function(xy, parentMenu, /* private: */_e){
3513         this.parentMenu = parentMenu;
3514         if(!this.el){
3515             this.render();
3516         }
3517         if(_e !== false){
3518             this.fireEvent("beforeshow", this);
3519             //xy = this.el.adjustForConstraints(xy);
3520         }
3521         
3522         //this.el.show();
3523         this.hideMenuItems();
3524         this.hidden = false;
3525         this.triggerEl.addClass('open');
3526         this.el.addClass('show');
3527         
3528         // reassign x when hitting right
3529         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3530             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3531         }
3532         
3533         // reassign y when hitting bottom
3534         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3535             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3536         }
3537         
3538         // but the list may align on trigger left or trigger top... should it be a properity?
3539         
3540         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3541             this.el.setXY(xy);
3542         }
3543         
3544         this.focus();
3545         this.fireEvent("show", this);
3546     },
3547     
3548     focus : function(){
3549         return;
3550         if(!this.hidden){
3551             this.doFocus.defer(50, this);
3552         }
3553     },
3554
3555     doFocus : function(){
3556         if(!this.hidden){
3557             this.focusEl.focus();
3558         }
3559     },
3560
3561     /**
3562      * Hides this menu and optionally all parent menus
3563      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3564      */
3565     hide : function(deep)
3566     {
3567         if (false === this.fireEvent("beforehide", this)) {
3568             Roo.log("hide canceled");
3569             return;
3570         }
3571         this.hideMenuItems();
3572         if(this.el && this.isVisible()){
3573            
3574             if(this.activeItem){
3575                 this.activeItem.deactivate();
3576                 this.activeItem = null;
3577             }
3578             this.triggerEl.removeClass('open');;
3579             this.el.removeClass('show');
3580             this.hidden = true;
3581             this.fireEvent("hide", this);
3582         }
3583         if(deep === true && this.parentMenu){
3584             this.parentMenu.hide(true);
3585         }
3586     },
3587     
3588     onTriggerClick : function(e)
3589     {
3590         Roo.log('trigger click');
3591         
3592         var target = e.getTarget();
3593         
3594         Roo.log(target.nodeName.toLowerCase());
3595         
3596         if(target.nodeName.toLowerCase() === 'i'){
3597             e.preventDefault();
3598         }
3599         
3600     },
3601     
3602     onTriggerPress  : function(e)
3603     {
3604         Roo.log('trigger press');
3605         //Roo.log(e.getTarget());
3606        // Roo.log(this.triggerEl.dom);
3607        
3608         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3609         var pel = Roo.get(e.getTarget());
3610         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3611             Roo.log('is treeview or dropdown?');
3612             return;
3613         }
3614         
3615         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3616             return;
3617         }
3618         
3619         if (this.isVisible()) {
3620             Roo.log('hide');
3621             this.hide();
3622         } else {
3623             Roo.log('show');
3624             this.show(this.triggerEl, '?', false);
3625         }
3626         
3627         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3628             e.stopEvent();
3629         }
3630         
3631     },
3632        
3633     
3634     hideMenuItems : function()
3635     {
3636         Roo.log("hide Menu Items");
3637         if (!this.el) { 
3638             return;
3639         }
3640         
3641         this.el.select('.open',true).each(function(aa) {
3642             
3643             aa.removeClass('open');
3644          
3645         });
3646     },
3647     addxtypeChild : function (tree, cntr) {
3648         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3649           
3650         this.menuitems.add(comp);
3651         return comp;
3652
3653     },
3654     getEl : function()
3655     {
3656         Roo.log(this.el);
3657         return this.el;
3658     },
3659     
3660     clear : function()
3661     {
3662         this.getEl().dom.innerHTML = '';
3663         this.menuitems.clear();
3664     }
3665 });
3666
3667  
3668  /*
3669  * - LGPL
3670  *
3671  * menu item
3672  * 
3673  */
3674
3675
3676 /**
3677  * @class Roo.bootstrap.MenuItem
3678  * @extends Roo.bootstrap.Component
3679  * Bootstrap MenuItem class
3680  * @cfg {String} html the menu label
3681  * @cfg {String} href the link
3682  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3683  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3684  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3685  * @cfg {String} fa favicon to show on left of menu item.
3686  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3687  * 
3688  * 
3689  * @constructor
3690  * Create a new MenuItem
3691  * @param {Object} config The config object
3692  */
3693
3694
3695 Roo.bootstrap.MenuItem = function(config){
3696     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3697     this.addEvents({
3698         // raw events
3699         /**
3700          * @event click
3701          * The raw click event for the entire grid.
3702          * @param {Roo.bootstrap.MenuItem} this
3703          * @param {Roo.EventObject} e
3704          */
3705         "click" : true
3706     });
3707 };
3708
3709 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3710     
3711     href : false,
3712     html : false,
3713     preventDefault: false,
3714     isContainer : false,
3715     active : false,
3716     fa: false,
3717     
3718     getAutoCreate : function(){
3719         
3720         if(this.isContainer){
3721             return {
3722                 tag: 'li',
3723                 cls: 'dropdown-menu-item '
3724             };
3725         }
3726         var ctag = {
3727             tag: 'span',
3728             html: 'Link'
3729         };
3730         
3731         var anc = {
3732             tag : 'a',
3733             cls : 'dropdown-item',
3734             href : '#',
3735             cn : [  ]
3736         };
3737         
3738         if (this.fa !== false) {
3739             anc.cn.push({
3740                 tag : 'i',
3741                 cls : 'fa fa-' + this.fa
3742             });
3743         }
3744         
3745         anc.cn.push(ctag);
3746         
3747         
3748         var cfg= {
3749             tag: 'li',
3750             cls: 'dropdown-menu-item',
3751             cn: [ anc ]
3752         };
3753         if (this.parent().type == 'treeview') {
3754             cfg.cls = 'treeview-menu';
3755         }
3756         if (this.active) {
3757             cfg.cls += ' active';
3758         }
3759         
3760         
3761         
3762         anc.href = this.href || cfg.cn[0].href ;
3763         ctag.html = this.html || cfg.cn[0].html ;
3764         return cfg;
3765     },
3766     
3767     initEvents: function()
3768     {
3769         if (this.parent().type == 'treeview') {
3770             this.el.select('a').on('click', this.onClick, this);
3771         }
3772         
3773         if (this.menu) {
3774             this.menu.parentType = this.xtype;
3775             this.menu.triggerEl = this.el;
3776             this.menu = this.addxtype(Roo.apply({}, this.menu));
3777         }
3778         
3779     },
3780     onClick : function(e)
3781     {
3782         Roo.log('item on click ');
3783         
3784         if(this.preventDefault){
3785             e.preventDefault();
3786         }
3787         //this.parent().hideMenuItems();
3788         
3789         this.fireEvent('click', this, e);
3790     },
3791     getEl : function()
3792     {
3793         return this.el;
3794     } 
3795 });
3796
3797  
3798
3799  /*
3800  * - LGPL
3801  *
3802  * menu separator
3803  * 
3804  */
3805
3806
3807 /**
3808  * @class Roo.bootstrap.MenuSeparator
3809  * @extends Roo.bootstrap.Component
3810  * Bootstrap MenuSeparator class
3811  * 
3812  * @constructor
3813  * Create a new MenuItem
3814  * @param {Object} config The config object
3815  */
3816
3817
3818 Roo.bootstrap.MenuSeparator = function(config){
3819     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3820 };
3821
3822 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3823     
3824     getAutoCreate : function(){
3825         var cfg = {
3826             cls: 'divider',
3827             tag : 'li'
3828         };
3829         
3830         return cfg;
3831     }
3832    
3833 });
3834
3835  
3836
3837  
3838 /*
3839 * Licence: LGPL
3840 */
3841
3842 /**
3843  * @class Roo.bootstrap.Modal
3844  * @extends Roo.bootstrap.Component
3845  * Bootstrap Modal class
3846  * @cfg {String} title Title of dialog
3847  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3848  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3849  * @cfg {Boolean} specificTitle default false
3850  * @cfg {Array} buttons Array of buttons or standard button set..
3851  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3852  * @cfg {Boolean} animate default true
3853  * @cfg {Boolean} allow_close default true
3854  * @cfg {Boolean} fitwindow default false
3855  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3856  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3857  * @cfg {String} size (sm|lg|xl) default empty
3858  * @cfg {Number} max_width set the max width of modal
3859  * @cfg {Boolean} editableTitle can the title be edited
3860
3861  *
3862  *
3863  * @constructor
3864  * Create a new Modal Dialog
3865  * @param {Object} config The config object
3866  */
3867
3868 Roo.bootstrap.Modal = function(config){
3869     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3870     this.addEvents({
3871         // raw events
3872         /**
3873          * @event btnclick
3874          * The raw btnclick event for the button
3875          * @param {Roo.EventObject} e
3876          */
3877         "btnclick" : true,
3878         /**
3879          * @event resize
3880          * Fire when dialog resize
3881          * @param {Roo.bootstrap.Modal} this
3882          * @param {Roo.EventObject} e
3883          */
3884         "resize" : true,
3885         /**
3886          * @event titlechanged
3887          * Fire when the editable title has been changed
3888          * @param {Roo.bootstrap.Modal} this
3889          * @param {Roo.EventObject} value
3890          */
3891         "titlechanged" : true 
3892         
3893     });
3894     this.buttons = this.buttons || [];
3895
3896     if (this.tmpl) {
3897         this.tmpl = Roo.factory(this.tmpl);
3898     }
3899
3900 };
3901
3902 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3903
3904     title : 'test dialog',
3905
3906     buttons : false,
3907
3908     // set on load...
3909
3910     html: false,
3911
3912     tmp: false,
3913
3914     specificTitle: false,
3915
3916     buttonPosition: 'right',
3917
3918     allow_close : true,
3919
3920     animate : true,
3921
3922     fitwindow: false,
3923     
3924      // private
3925     dialogEl: false,
3926     bodyEl:  false,
3927     footerEl:  false,
3928     titleEl:  false,
3929     closeEl:  false,
3930
3931     size: '',
3932     
3933     max_width: 0,
3934     
3935     max_height: 0,
3936     
3937     fit_content: false,
3938     editableTitle  : false,
3939
3940     onRender : function(ct, position)
3941     {
3942         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
3943
3944         if(!this.el){
3945             var cfg = Roo.apply({},  this.getAutoCreate());
3946             cfg.id = Roo.id();
3947             //if(!cfg.name){
3948             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
3949             //}
3950             //if (!cfg.name.length) {
3951             //    delete cfg.name;
3952            // }
3953             if (this.cls) {
3954                 cfg.cls += ' ' + this.cls;
3955             }
3956             if (this.style) {
3957                 cfg.style = this.style;
3958             }
3959             this.el = Roo.get(document.body).createChild(cfg, position);
3960         }
3961         //var type = this.el.dom.type;
3962
3963
3964         if(this.tabIndex !== undefined){
3965             this.el.dom.setAttribute('tabIndex', this.tabIndex);
3966         }
3967
3968         this.dialogEl = this.el.select('.modal-dialog',true).first();
3969         this.bodyEl = this.el.select('.modal-body',true).first();
3970         this.closeEl = this.el.select('.modal-header .close', true).first();
3971         this.headerEl = this.el.select('.modal-header',true).first();
3972         this.titleEl = this.el.select('.modal-title',true).first();
3973         this.footerEl = this.el.select('.modal-footer',true).first();
3974
3975         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
3976         
3977         //this.el.addClass("x-dlg-modal");
3978
3979         if (this.buttons.length) {
3980             Roo.each(this.buttons, function(bb) {
3981                 var b = Roo.apply({}, bb);
3982                 b.xns = b.xns || Roo.bootstrap;
3983                 b.xtype = b.xtype || 'Button';
3984                 if (typeof(b.listeners) == 'undefined') {
3985                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
3986                 }
3987
3988                 var btn = Roo.factory(b);
3989
3990                 btn.render(this.getButtonContainer());
3991
3992             },this);
3993         }
3994         // render the children.
3995         var nitems = [];
3996
3997         if(typeof(this.items) != 'undefined'){
3998             var items = this.items;
3999             delete this.items;
4000
4001             for(var i =0;i < items.length;i++) {
4002                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4003             }
4004         }
4005
4006         this.items = nitems;
4007
4008         // where are these used - they used to be body/close/footer
4009
4010
4011         this.initEvents();
4012         //this.el.addClass([this.fieldClass, this.cls]);
4013
4014     },
4015
4016     getAutoCreate : function()
4017     {
4018         // we will default to modal-body-overflow - might need to remove or make optional later.
4019         var bdy = {
4020                 cls : 'modal-body enable-modal-body-overflow ', 
4021                 html : this.html || ''
4022         };
4023
4024         var title = {
4025             tag: 'h4',
4026             cls : 'modal-title',
4027             html : this.title
4028         };
4029
4030         if(this.specificTitle){ // WTF is this?
4031             title = this.title;
4032         }
4033
4034         var header = [];
4035         if (this.allow_close && Roo.bootstrap.version == 3) {
4036             header.push({
4037                 tag: 'button',
4038                 cls : 'close',
4039                 html : '&times'
4040             });
4041         }
4042
4043         header.push(title);
4044
4045         if (this.editableTitle) {
4046             header.push({
4047                 cls: 'form-control roo-editable-title d-none',
4048                 tag: 'input',
4049                 type: 'text'
4050             });
4051         }
4052         
4053         if (this.allow_close && Roo.bootstrap.version == 4) {
4054             header.push({
4055                 tag: 'button',
4056                 cls : 'close',
4057                 html : '&times'
4058             });
4059         }
4060         
4061         var size = '';
4062
4063         if(this.size.length){
4064             size = 'modal-' + this.size;
4065         }
4066         
4067         var footer = Roo.bootstrap.version == 3 ?
4068             {
4069                 cls : 'modal-footer',
4070                 cn : [
4071                     {
4072                         tag: 'div',
4073                         cls: 'btn-' + this.buttonPosition
4074                     }
4075                 ]
4076
4077             } :
4078             {  // BS4 uses mr-auto on left buttons....
4079                 cls : 'modal-footer'
4080             };
4081
4082             
4083
4084         
4085         
4086         var modal = {
4087             cls: "modal",
4088              cn : [
4089                 {
4090                     cls: "modal-dialog " + size,
4091                     cn : [
4092                         {
4093                             cls : "modal-content",
4094                             cn : [
4095                                 {
4096                                     cls : 'modal-header',
4097                                     cn : header
4098                                 },
4099                                 bdy,
4100                                 footer
4101                             ]
4102
4103                         }
4104                     ]
4105
4106                 }
4107             ]
4108         };
4109
4110         if(this.animate){
4111             modal.cls += ' fade';
4112         }
4113
4114         return modal;
4115
4116     },
4117     getChildContainer : function() {
4118
4119          return this.bodyEl;
4120
4121     },
4122     getButtonContainer : function() {
4123         
4124          return Roo.bootstrap.version == 4 ?
4125             this.el.select('.modal-footer',true).first()
4126             : this.el.select('.modal-footer div',true).first();
4127
4128     },
4129     initEvents : function()
4130     {
4131         if (this.allow_close) {
4132             this.closeEl.on('click', this.hide, this);
4133         }
4134         Roo.EventManager.onWindowResize(this.resize, this, true);
4135         if (this.editableTitle) {
4136             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4137             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4138             this.headerEditEl.on('keyup', function(e) {
4139                     if(e.isNavKeyPress()){
4140                             this.toggleHeaderInput(false)
4141                     }
4142                 }, this);
4143             this.headerEditEl.on('blur', function(e) {
4144                 this.toggleHeaderInput(false)
4145             },this);
4146         }
4147
4148     },
4149   
4150
4151     resize : function()
4152     {
4153         this.maskEl.setSize(
4154             Roo.lib.Dom.getViewWidth(true),
4155             Roo.lib.Dom.getViewHeight(true)
4156         );
4157         
4158         if (this.fitwindow) {
4159             
4160            
4161             this.setSize(
4162                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4163                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4164             );
4165             return;
4166         }
4167         
4168         if(this.max_width !== 0) {
4169             
4170             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4171             
4172             if(this.height) {
4173                 this.setSize(w, this.height);
4174                 return;
4175             }
4176             
4177             if(this.max_height) {
4178                 this.setSize(w,Math.min(
4179                     this.max_height,
4180                     Roo.lib.Dom.getViewportHeight(true) - 60
4181                 ));
4182                 
4183                 return;
4184             }
4185             
4186             if(!this.fit_content) {
4187                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4188                 return;
4189             }
4190             
4191             this.setSize(w, Math.min(
4192                 60 +
4193                 this.headerEl.getHeight() + 
4194                 this.footerEl.getHeight() + 
4195                 this.getChildHeight(this.bodyEl.dom.childNodes),
4196                 Roo.lib.Dom.getViewportHeight(true) - 60)
4197             );
4198         }
4199         
4200     },
4201
4202     setSize : function(w,h)
4203     {
4204         if (!w && !h) {
4205             return;
4206         }
4207         
4208         this.resizeTo(w,h);
4209     },
4210
4211     show : function() {
4212
4213         if (!this.rendered) {
4214             this.render();
4215         }
4216
4217         //this.el.setStyle('display', 'block');
4218         this.el.removeClass('hideing');
4219         this.el.dom.style.display='block';
4220         
4221         Roo.get(document.body).addClass('modal-open');
4222  
4223         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4224             
4225             (function(){
4226                 this.el.addClass('show');
4227                 this.el.addClass('in');
4228             }).defer(50, this);
4229         }else{
4230             this.el.addClass('show');
4231             this.el.addClass('in');
4232         }
4233
4234         // not sure how we can show data in here..
4235         //if (this.tmpl) {
4236         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4237         //}
4238
4239         Roo.get(document.body).addClass("x-body-masked");
4240         
4241         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4242         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4243         this.maskEl.dom.style.display = 'block';
4244         this.maskEl.addClass('show');
4245         
4246         
4247         this.resize();
4248         
4249         this.fireEvent('show', this);
4250
4251         // set zindex here - otherwise it appears to be ignored...
4252         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4253
4254         (function () {
4255             this.items.forEach( function(e) {
4256                 e.layout ? e.layout() : false;
4257
4258             });
4259         }).defer(100,this);
4260
4261     },
4262     hide : function()
4263     {
4264         if(this.fireEvent("beforehide", this) !== false){
4265             
4266             this.maskEl.removeClass('show');
4267             
4268             this.maskEl.dom.style.display = '';
4269             Roo.get(document.body).removeClass("x-body-masked");
4270             this.el.removeClass('in');
4271             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4272
4273             if(this.animate){ // why
4274                 this.el.addClass('hideing');
4275                 this.el.removeClass('show');
4276                 (function(){
4277                     if (!this.el.hasClass('hideing')) {
4278                         return; // it's been shown again...
4279                     }
4280                     
4281                     this.el.dom.style.display='';
4282
4283                     Roo.get(document.body).removeClass('modal-open');
4284                     this.el.removeClass('hideing');
4285                 }).defer(150,this);
4286                 
4287             }else{
4288                 this.el.removeClass('show');
4289                 this.el.dom.style.display='';
4290                 Roo.get(document.body).removeClass('modal-open');
4291
4292             }
4293             this.fireEvent('hide', this);
4294         }
4295     },
4296     isVisible : function()
4297     {
4298         
4299         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4300         
4301     },
4302
4303     addButton : function(str, cb)
4304     {
4305
4306
4307         var b = Roo.apply({}, { html : str } );
4308         b.xns = b.xns || Roo.bootstrap;
4309         b.xtype = b.xtype || 'Button';
4310         if (typeof(b.listeners) == 'undefined') {
4311             b.listeners = { click : cb.createDelegate(this)  };
4312         }
4313
4314         var btn = Roo.factory(b);
4315
4316         btn.render(this.getButtonContainer());
4317
4318         return btn;
4319
4320     },
4321
4322     setDefaultButton : function(btn)
4323     {
4324         //this.el.select('.modal-footer').()
4325     },
4326
4327     resizeTo: function(w,h)
4328     {
4329         this.dialogEl.setWidth(w);
4330         
4331         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4332
4333         this.bodyEl.setHeight(h - diff);
4334         
4335         this.fireEvent('resize', this);
4336     },
4337     
4338     setContentSize  : function(w, h)
4339     {
4340
4341     },
4342     onButtonClick: function(btn,e)
4343     {
4344         //Roo.log([a,b,c]);
4345         this.fireEvent('btnclick', btn.name, e);
4346     },
4347      /**
4348      * Set the title of the Dialog
4349      * @param {String} str new Title
4350      */
4351     setTitle: function(str) {
4352         this.titleEl.dom.innerHTML = str;
4353         this.title = str;
4354     },
4355     /**
4356      * Set the body of the Dialog
4357      * @param {String} str new Title
4358      */
4359     setBody: function(str) {
4360         this.bodyEl.dom.innerHTML = str;
4361     },
4362     /**
4363      * Set the body of the Dialog using the template
4364      * @param {Obj} data - apply this data to the template and replace the body contents.
4365      */
4366     applyBody: function(obj)
4367     {
4368         if (!this.tmpl) {
4369             Roo.log("Error - using apply Body without a template");
4370             //code
4371         }
4372         this.tmpl.overwrite(this.bodyEl, obj);
4373     },
4374     
4375     getChildHeight : function(child_nodes)
4376     {
4377         if(
4378             !child_nodes ||
4379             child_nodes.length == 0
4380         ) {
4381             return 0;
4382         }
4383         
4384         var child_height = 0;
4385         
4386         for(var i = 0; i < child_nodes.length; i++) {
4387             
4388             /*
4389             * for modal with tabs...
4390             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4391                 
4392                 var layout_childs = child_nodes[i].childNodes;
4393                 
4394                 for(var j = 0; j < layout_childs.length; j++) {
4395                     
4396                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4397                         
4398                         var layout_body_childs = layout_childs[j].childNodes;
4399                         
4400                         for(var k = 0; k < layout_body_childs.length; k++) {
4401                             
4402                             if(layout_body_childs[k].classList.contains('navbar')) {
4403                                 child_height += layout_body_childs[k].offsetHeight;
4404                                 continue;
4405                             }
4406                             
4407                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4408                                 
4409                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4410                                 
4411                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4412                                     
4413                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4414                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4415                                         continue;
4416                                     }
4417                                     
4418                                 }
4419                                 
4420                             }
4421                             
4422                         }
4423                     }
4424                 }
4425                 continue;
4426             }
4427             */
4428             
4429             child_height += child_nodes[i].offsetHeight;
4430             // Roo.log(child_nodes[i].offsetHeight);
4431         }
4432         
4433         return child_height;
4434     },
4435     toggleHeaderInput : function(is_edit)
4436     {
4437         
4438         if (is_edit && this.is_header_editing) {
4439             return; // already editing..
4440         }
4441         if (is_edit) {
4442     
4443             this.headerEditEl.dom.value = this.title;
4444             this.headerEditEl.removeClass('d-none');
4445             this.headerEditEl.dom.focus();
4446             this.titleEl.addClass('d-none');
4447             
4448             this.is_header_editing = true;
4449             return
4450         }
4451         // flip back to not editing.
4452         this.title = this.headerEditEl.dom.value;
4453         this.headerEditEl.addClass('d-none');
4454         this.titleEl.removeClass('d-none');
4455         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4456         this.is_header_editing = false;
4457         this.fireEvent('titlechanged', this, this.title);
4458     
4459             
4460         
4461     }
4462
4463 });
4464
4465
4466 Roo.apply(Roo.bootstrap.Modal,  {
4467     /**
4468          * Button config that displays a single OK button
4469          * @type Object
4470          */
4471         OK :  [{
4472             name : 'ok',
4473             weight : 'primary',
4474             html : 'OK'
4475         }],
4476         /**
4477          * Button config that displays Yes and No buttons
4478          * @type Object
4479          */
4480         YESNO : [
4481             {
4482                 name  : 'no',
4483                 html : 'No'
4484             },
4485             {
4486                 name  :'yes',
4487                 weight : 'primary',
4488                 html : 'Yes'
4489             }
4490         ],
4491
4492         /**
4493          * Button config that displays OK and Cancel buttons
4494          * @type Object
4495          */
4496         OKCANCEL : [
4497             {
4498                name : 'cancel',
4499                 html : 'Cancel'
4500             },
4501             {
4502                 name : 'ok',
4503                 weight : 'primary',
4504                 html : 'OK'
4505             }
4506         ],
4507         /**
4508          * Button config that displays Yes, No and Cancel buttons
4509          * @type Object
4510          */
4511         YESNOCANCEL : [
4512             {
4513                 name : 'yes',
4514                 weight : 'primary',
4515                 html : 'Yes'
4516             },
4517             {
4518                 name : 'no',
4519                 html : 'No'
4520             },
4521             {
4522                 name : 'cancel',
4523                 html : 'Cancel'
4524             }
4525         ],
4526         
4527         zIndex : 10001
4528 });
4529
4530 /*
4531  * - LGPL
4532  *
4533  * messagebox - can be used as a replace
4534  * 
4535  */
4536 /**
4537  * @class Roo.MessageBox
4538  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4539  * Example usage:
4540  *<pre><code>
4541 // Basic alert:
4542 Roo.Msg.alert('Status', 'Changes saved successfully.');
4543
4544 // Prompt for user data:
4545 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4546     if (btn == 'ok'){
4547         // process text value...
4548     }
4549 });
4550
4551 // Show a dialog using config options:
4552 Roo.Msg.show({
4553    title:'Save Changes?',
4554    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4555    buttons: Roo.Msg.YESNOCANCEL,
4556    fn: processResult,
4557    animEl: 'elId'
4558 });
4559 </code></pre>
4560  * @singleton
4561  */
4562 Roo.bootstrap.MessageBox = function(){
4563     var dlg, opt, mask, waitTimer;
4564     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4565     var buttons, activeTextEl, bwidth;
4566
4567     
4568     // private
4569     var handleButton = function(button){
4570         dlg.hide();
4571         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4572     };
4573
4574     // private
4575     var handleHide = function(){
4576         if(opt && opt.cls){
4577             dlg.el.removeClass(opt.cls);
4578         }
4579         //if(waitTimer){
4580         //    Roo.TaskMgr.stop(waitTimer);
4581         //    waitTimer = null;
4582         //}
4583     };
4584
4585     // private
4586     var updateButtons = function(b){
4587         var width = 0;
4588         if(!b){
4589             buttons["ok"].hide();
4590             buttons["cancel"].hide();
4591             buttons["yes"].hide();
4592             buttons["no"].hide();
4593             dlg.footerEl.hide();
4594             
4595             return width;
4596         }
4597         dlg.footerEl.show();
4598         for(var k in buttons){
4599             if(typeof buttons[k] != "function"){
4600                 if(b[k]){
4601                     buttons[k].show();
4602                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4603                     width += buttons[k].el.getWidth()+15;
4604                 }else{
4605                     buttons[k].hide();
4606                 }
4607             }
4608         }
4609         return width;
4610     };
4611
4612     // private
4613     var handleEsc = function(d, k, e){
4614         if(opt && opt.closable !== false){
4615             dlg.hide();
4616         }
4617         if(e){
4618             e.stopEvent();
4619         }
4620     };
4621
4622     return {
4623         /**
4624          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4625          * @return {Roo.BasicDialog} The BasicDialog element
4626          */
4627         getDialog : function(){
4628            if(!dlg){
4629                 dlg = new Roo.bootstrap.Modal( {
4630                     //draggable: true,
4631                     //resizable:false,
4632                     //constraintoviewport:false,
4633                     //fixedcenter:true,
4634                     //collapsible : false,
4635                     //shim:true,
4636                     //modal: true,
4637                 //    width: 'auto',
4638                   //  height:100,
4639                     //buttonAlign:"center",
4640                     closeClick : function(){
4641                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4642                             handleButton("no");
4643                         }else{
4644                             handleButton("cancel");
4645                         }
4646                     }
4647                 });
4648                 dlg.render();
4649                 dlg.on("hide", handleHide);
4650                 mask = dlg.mask;
4651                 //dlg.addKeyListener(27, handleEsc);
4652                 buttons = {};
4653                 this.buttons = buttons;
4654                 var bt = this.buttonText;
4655                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4656                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4657                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4658                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4659                 //Roo.log(buttons);
4660                 bodyEl = dlg.bodyEl.createChild({
4661
4662                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4663                         '<textarea class="roo-mb-textarea"></textarea>' +
4664                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4665                 });
4666                 msgEl = bodyEl.dom.firstChild;
4667                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4668                 textboxEl.enableDisplayMode();
4669                 textboxEl.addKeyListener([10,13], function(){
4670                     if(dlg.isVisible() && opt && opt.buttons){
4671                         if(opt.buttons.ok){
4672                             handleButton("ok");
4673                         }else if(opt.buttons.yes){
4674                             handleButton("yes");
4675                         }
4676                     }
4677                 });
4678                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4679                 textareaEl.enableDisplayMode();
4680                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4681                 progressEl.enableDisplayMode();
4682                 
4683                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4684                 var pf = progressEl.dom.firstChild;
4685                 if (pf) {
4686                     pp = Roo.get(pf.firstChild);
4687                     pp.setHeight(pf.offsetHeight);
4688                 }
4689                 
4690             }
4691             return dlg;
4692         },
4693
4694         /**
4695          * Updates the message box body text
4696          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4697          * the XHTML-compliant non-breaking space character '&amp;#160;')
4698          * @return {Roo.MessageBox} This message box
4699          */
4700         updateText : function(text)
4701         {
4702             if(!dlg.isVisible() && !opt.width){
4703                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4704                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4705             }
4706             msgEl.innerHTML = text || '&#160;';
4707       
4708             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4709             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4710             var w = Math.max(
4711                     Math.min(opt.width || cw , this.maxWidth), 
4712                     Math.max(opt.minWidth || this.minWidth, bwidth)
4713             );
4714             if(opt.prompt){
4715                 activeTextEl.setWidth(w);
4716             }
4717             if(dlg.isVisible()){
4718                 dlg.fixedcenter = false;
4719             }
4720             // to big, make it scroll. = But as usual stupid IE does not support
4721             // !important..
4722             
4723             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4724                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4725                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4726             } else {
4727                 bodyEl.dom.style.height = '';
4728                 bodyEl.dom.style.overflowY = '';
4729             }
4730             if (cw > w) {
4731                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4732             } else {
4733                 bodyEl.dom.style.overflowX = '';
4734             }
4735             
4736             dlg.setContentSize(w, bodyEl.getHeight());
4737             if(dlg.isVisible()){
4738                 dlg.fixedcenter = true;
4739             }
4740             return this;
4741         },
4742
4743         /**
4744          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4745          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4746          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4747          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4748          * @return {Roo.MessageBox} This message box
4749          */
4750         updateProgress : function(value, text){
4751             if(text){
4752                 this.updateText(text);
4753             }
4754             
4755             if (pp) { // weird bug on my firefox - for some reason this is not defined
4756                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4757                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4758             }
4759             return this;
4760         },        
4761
4762         /**
4763          * Returns true if the message box is currently displayed
4764          * @return {Boolean} True if the message box is visible, else false
4765          */
4766         isVisible : function(){
4767             return dlg && dlg.isVisible();  
4768         },
4769
4770         /**
4771          * Hides the message box if it is displayed
4772          */
4773         hide : function(){
4774             if(this.isVisible()){
4775                 dlg.hide();
4776             }  
4777         },
4778
4779         /**
4780          * Displays a new message box, or reinitializes an existing message box, based on the config options
4781          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4782          * The following config object properties are supported:
4783          * <pre>
4784 Property    Type             Description
4785 ----------  ---------------  ------------------------------------------------------------------------------------
4786 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4787                                    closes (defaults to undefined)
4788 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4789                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4790 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4791                                    progress and wait dialogs will ignore this property and always hide the
4792                                    close button as they can only be closed programmatically.
4793 cls               String           A custom CSS class to apply to the message box element
4794 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4795                                    displayed (defaults to 75)
4796 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4797                                    function will be btn (the name of the button that was clicked, if applicable,
4798                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4799                                    Progress and wait dialogs will ignore this option since they do not respond to
4800                                    user actions and can only be closed programmatically, so any required function
4801                                    should be called by the same code after it closes the dialog.
4802 icon              String           A CSS class that provides a background image to be used as an icon for
4803                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4804 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4805 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4806 modal             Boolean          False to allow user interaction with the page while the message box is
4807                                    displayed (defaults to true)
4808 msg               String           A string that will replace the existing message box body text (defaults
4809                                    to the XHTML-compliant non-breaking space character '&#160;')
4810 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4811 progress          Boolean          True to display a progress bar (defaults to false)
4812 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4813 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4814 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4815 title             String           The title text
4816 value             String           The string value to set into the active textbox element if displayed
4817 wait              Boolean          True to display a progress bar (defaults to false)
4818 width             Number           The width of the dialog in pixels
4819 </pre>
4820          *
4821          * Example usage:
4822          * <pre><code>
4823 Roo.Msg.show({
4824    title: 'Address',
4825    msg: 'Please enter your address:',
4826    width: 300,
4827    buttons: Roo.MessageBox.OKCANCEL,
4828    multiline: true,
4829    fn: saveAddress,
4830    animEl: 'addAddressBtn'
4831 });
4832 </code></pre>
4833          * @param {Object} config Configuration options
4834          * @return {Roo.MessageBox} This message box
4835          */
4836         show : function(options)
4837         {
4838             
4839             // this causes nightmares if you show one dialog after another
4840             // especially on callbacks..
4841              
4842             if(this.isVisible()){
4843                 
4844                 this.hide();
4845                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4846                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4847                 Roo.log("New Dialog Message:" +  options.msg )
4848                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4849                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4850                 
4851             }
4852             var d = this.getDialog();
4853             opt = options;
4854             d.setTitle(opt.title || "&#160;");
4855             d.closeEl.setDisplayed(opt.closable !== false);
4856             activeTextEl = textboxEl;
4857             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4858             if(opt.prompt){
4859                 if(opt.multiline){
4860                     textboxEl.hide();
4861                     textareaEl.show();
4862                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4863                         opt.multiline : this.defaultTextHeight);
4864                     activeTextEl = textareaEl;
4865                 }else{
4866                     textboxEl.show();
4867                     textareaEl.hide();
4868                 }
4869             }else{
4870                 textboxEl.hide();
4871                 textareaEl.hide();
4872             }
4873             progressEl.setDisplayed(opt.progress === true);
4874             if (opt.progress) {
4875                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4876             }
4877             this.updateProgress(0);
4878             activeTextEl.dom.value = opt.value || "";
4879             if(opt.prompt){
4880                 dlg.setDefaultButton(activeTextEl);
4881             }else{
4882                 var bs = opt.buttons;
4883                 var db = null;
4884                 if(bs && bs.ok){
4885                     db = buttons["ok"];
4886                 }else if(bs && bs.yes){
4887                     db = buttons["yes"];
4888                 }
4889                 dlg.setDefaultButton(db);
4890             }
4891             bwidth = updateButtons(opt.buttons);
4892             this.updateText(opt.msg);
4893             if(opt.cls){
4894                 d.el.addClass(opt.cls);
4895             }
4896             d.proxyDrag = opt.proxyDrag === true;
4897             d.modal = opt.modal !== false;
4898             d.mask = opt.modal !== false ? mask : false;
4899             if(!d.isVisible()){
4900                 // force it to the end of the z-index stack so it gets a cursor in FF
4901                 document.body.appendChild(dlg.el.dom);
4902                 d.animateTarget = null;
4903                 d.show(options.animEl);
4904             }
4905             return this;
4906         },
4907
4908         /**
4909          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
4910          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
4911          * and closing the message box when the process is complete.
4912          * @param {String} title The title bar text
4913          * @param {String} msg The message box body text
4914          * @return {Roo.MessageBox} This message box
4915          */
4916         progress : function(title, msg){
4917             this.show({
4918                 title : title,
4919                 msg : msg,
4920                 buttons: false,
4921                 progress:true,
4922                 closable:false,
4923                 minWidth: this.minProgressWidth,
4924                 modal : true
4925             });
4926             return this;
4927         },
4928
4929         /**
4930          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
4931          * If a callback function is passed it will be called after the user clicks the button, and the
4932          * id of the button that was clicked will be passed as the only parameter to the callback
4933          * (could also be the top-right close button).
4934          * @param {String} title The title bar text
4935          * @param {String} msg The message box body text
4936          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4937          * @param {Object} scope (optional) The scope of the callback function
4938          * @return {Roo.MessageBox} This message box
4939          */
4940         alert : function(title, msg, fn, scope)
4941         {
4942             this.show({
4943                 title : title,
4944                 msg : msg,
4945                 buttons: this.OK,
4946                 fn: fn,
4947                 closable : false,
4948                 scope : scope,
4949                 modal : true
4950             });
4951             return this;
4952         },
4953
4954         /**
4955          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
4956          * interaction while waiting for a long-running process to complete that does not have defined intervals.
4957          * You are responsible for closing the message box when the process is complete.
4958          * @param {String} msg The message box body text
4959          * @param {String} title (optional) The title bar text
4960          * @return {Roo.MessageBox} This message box
4961          */
4962         wait : function(msg, title){
4963             this.show({
4964                 title : title,
4965                 msg : msg,
4966                 buttons: false,
4967                 closable:false,
4968                 progress:true,
4969                 modal:true,
4970                 width:300,
4971                 wait:true
4972             });
4973             waitTimer = Roo.TaskMgr.start({
4974                 run: function(i){
4975                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
4976                 },
4977                 interval: 1000
4978             });
4979             return this;
4980         },
4981
4982         /**
4983          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
4984          * If a callback function is passed it will be called after the user clicks either button, and the id of the
4985          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
4986          * @param {String} title The title bar text
4987          * @param {String} msg The message box body text
4988          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4989          * @param {Object} scope (optional) The scope of the callback function
4990          * @return {Roo.MessageBox} This message box
4991          */
4992         confirm : function(title, msg, fn, scope){
4993             this.show({
4994                 title : title,
4995                 msg : msg,
4996                 buttons: this.YESNO,
4997                 fn: fn,
4998                 scope : scope,
4999                 modal : true
5000             });
5001             return this;
5002         },
5003
5004         /**
5005          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5006          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5007          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5008          * (could also be the top-right close button) and the text that was entered will be passed as the two
5009          * parameters to the callback.
5010          * @param {String} title The title bar text
5011          * @param {String} msg The message box body text
5012          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5013          * @param {Object} scope (optional) The scope of the callback function
5014          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5015          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5016          * @return {Roo.MessageBox} This message box
5017          */
5018         prompt : function(title, msg, fn, scope, multiline){
5019             this.show({
5020                 title : title,
5021                 msg : msg,
5022                 buttons: this.OKCANCEL,
5023                 fn: fn,
5024                 minWidth:250,
5025                 scope : scope,
5026                 prompt:true,
5027                 multiline: multiline,
5028                 modal : true
5029             });
5030             return this;
5031         },
5032
5033         /**
5034          * Button config that displays a single OK button
5035          * @type Object
5036          */
5037         OK : {ok:true},
5038         /**
5039          * Button config that displays Yes and No buttons
5040          * @type Object
5041          */
5042         YESNO : {yes:true, no:true},
5043         /**
5044          * Button config that displays OK and Cancel buttons
5045          * @type Object
5046          */
5047         OKCANCEL : {ok:true, cancel:true},
5048         /**
5049          * Button config that displays Yes, No and Cancel buttons
5050          * @type Object
5051          */
5052         YESNOCANCEL : {yes:true, no:true, cancel:true},
5053
5054         /**
5055          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5056          * @type Number
5057          */
5058         defaultTextHeight : 75,
5059         /**
5060          * The maximum width in pixels of the message box (defaults to 600)
5061          * @type Number
5062          */
5063         maxWidth : 600,
5064         /**
5065          * The minimum width in pixels of the message box (defaults to 100)
5066          * @type Number
5067          */
5068         minWidth : 100,
5069         /**
5070          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5071          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5072          * @type Number
5073          */
5074         minProgressWidth : 250,
5075         /**
5076          * An object containing the default button text strings that can be overriden for localized language support.
5077          * Supported properties are: ok, cancel, yes and no.
5078          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5079          * @type Object
5080          */
5081         buttonText : {
5082             ok : "OK",
5083             cancel : "Cancel",
5084             yes : "Yes",
5085             no : "No"
5086         }
5087     };
5088 }();
5089
5090 /**
5091  * Shorthand for {@link Roo.MessageBox}
5092  */
5093 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5094 Roo.Msg = Roo.Msg || Roo.MessageBox;
5095 /*
5096  * - LGPL
5097  *
5098  * navbar
5099  * 
5100  */
5101
5102 /**
5103  * @class Roo.bootstrap.Navbar
5104  * @extends Roo.bootstrap.Component
5105  * Bootstrap Navbar class
5106
5107  * @constructor
5108  * Create a new Navbar
5109  * @param {Object} config The config object
5110  */
5111
5112
5113 Roo.bootstrap.Navbar = function(config){
5114     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5115     this.addEvents({
5116         // raw events
5117         /**
5118          * @event beforetoggle
5119          * Fire before toggle the menu
5120          * @param {Roo.EventObject} e
5121          */
5122         "beforetoggle" : true
5123     });
5124 };
5125
5126 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5127     
5128     
5129    
5130     // private
5131     navItems : false,
5132     loadMask : false,
5133     
5134     
5135     getAutoCreate : function(){
5136         
5137         
5138         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5139         
5140     },
5141     
5142     initEvents :function ()
5143     {
5144         //Roo.log(this.el.select('.navbar-toggle',true));
5145         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5146         
5147         var mark = {
5148             tag: "div",
5149             cls:"x-dlg-mask"
5150         };
5151         
5152         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5153         
5154         var size = this.el.getSize();
5155         this.maskEl.setSize(size.width, size.height);
5156         this.maskEl.enableDisplayMode("block");
5157         this.maskEl.hide();
5158         
5159         if(this.loadMask){
5160             this.maskEl.show();
5161         }
5162     },
5163     
5164     
5165     getChildContainer : function()
5166     {
5167         if (this.el && this.el.select('.collapse').getCount()) {
5168             return this.el.select('.collapse',true).first();
5169         }
5170         
5171         return this.el;
5172     },
5173     
5174     mask : function()
5175     {
5176         this.maskEl.show();
5177     },
5178     
5179     unmask : function()
5180     {
5181         this.maskEl.hide();
5182     },
5183     onToggle : function()
5184     {
5185         
5186         if(this.fireEvent('beforetoggle', this) === false){
5187             return;
5188         }
5189         var ce = this.el.select('.navbar-collapse',true).first();
5190       
5191         if (!ce.hasClass('show')) {
5192            this.expand();
5193         } else {
5194             this.collapse();
5195         }
5196         
5197         
5198     
5199     },
5200     /**
5201      * Expand the navbar pulldown 
5202      */
5203     expand : function ()
5204     {
5205        
5206         var ce = this.el.select('.navbar-collapse',true).first();
5207         if (ce.hasClass('collapsing')) {
5208             return;
5209         }
5210         ce.dom.style.height = '';
5211                // show it...
5212         ce.addClass('in'); // old...
5213         ce.removeClass('collapse');
5214         ce.addClass('show');
5215         var h = ce.getHeight();
5216         Roo.log(h);
5217         ce.removeClass('show');
5218         // at this point we should be able to see it..
5219         ce.addClass('collapsing');
5220         
5221         ce.setHeight(0); // resize it ...
5222         ce.on('transitionend', function() {
5223             //Roo.log('done transition');
5224             ce.removeClass('collapsing');
5225             ce.addClass('show');
5226             ce.removeClass('collapse');
5227
5228             ce.dom.style.height = '';
5229         }, this, { single: true} );
5230         ce.setHeight(h);
5231         ce.dom.scrollTop = 0;
5232     },
5233     /**
5234      * Collapse the navbar pulldown 
5235      */
5236     collapse : function()
5237     {
5238          var ce = this.el.select('.navbar-collapse',true).first();
5239        
5240         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5241             // it's collapsed or collapsing..
5242             return;
5243         }
5244         ce.removeClass('in'); // old...
5245         ce.setHeight(ce.getHeight());
5246         ce.removeClass('show');
5247         ce.addClass('collapsing');
5248         
5249         ce.on('transitionend', function() {
5250             ce.dom.style.height = '';
5251             ce.removeClass('collapsing');
5252             ce.addClass('collapse');
5253         }, this, { single: true} );
5254         ce.setHeight(0);
5255     }
5256     
5257     
5258     
5259 });
5260
5261
5262
5263  
5264
5265  /*
5266  * - LGPL
5267  *
5268  * navbar
5269  * 
5270  */
5271
5272 /**
5273  * @class Roo.bootstrap.NavSimplebar
5274  * @extends Roo.bootstrap.Navbar
5275  * Bootstrap Sidebar class
5276  *
5277  * @cfg {Boolean} inverse is inverted color
5278  * 
5279  * @cfg {String} type (nav | pills | tabs)
5280  * @cfg {Boolean} arrangement stacked | justified
5281  * @cfg {String} align (left | right) alignment
5282  * 
5283  * @cfg {Boolean} main (true|false) main nav bar? default false
5284  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5285  * 
5286  * @cfg {String} tag (header|footer|nav|div) default is nav 
5287
5288  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5289  * 
5290  * 
5291  * @constructor
5292  * Create a new Sidebar
5293  * @param {Object} config The config object
5294  */
5295
5296
5297 Roo.bootstrap.NavSimplebar = function(config){
5298     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5299 };
5300
5301 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5302     
5303     inverse: false,
5304     
5305     type: false,
5306     arrangement: '',
5307     align : false,
5308     
5309     weight : 'light',
5310     
5311     main : false,
5312     
5313     
5314     tag : false,
5315     
5316     
5317     getAutoCreate : function(){
5318         
5319         
5320         var cfg = {
5321             tag : this.tag || 'div',
5322             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5323         };
5324         if (['light','white'].indexOf(this.weight) > -1) {
5325             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5326         }
5327         cfg.cls += ' bg-' + this.weight;
5328         
5329         if (this.inverse) {
5330             cfg.cls += ' navbar-inverse';
5331             
5332         }
5333         
5334         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5335         
5336         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5337             return cfg;
5338         }
5339         
5340         
5341     
5342         
5343         cfg.cn = [
5344             {
5345                 cls: 'nav nav-' + this.xtype,
5346                 tag : 'ul'
5347             }
5348         ];
5349         
5350          
5351         this.type = this.type || 'nav';
5352         if (['tabs','pills'].indexOf(this.type) != -1) {
5353             cfg.cn[0].cls += ' nav-' + this.type
5354         
5355         
5356         } else {
5357             if (this.type!=='nav') {
5358                 Roo.log('nav type must be nav/tabs/pills')
5359             }
5360             cfg.cn[0].cls += ' navbar-nav'
5361         }
5362         
5363         
5364         
5365         
5366         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5367             cfg.cn[0].cls += ' nav-' + this.arrangement;
5368         }
5369         
5370         
5371         if (this.align === 'right') {
5372             cfg.cn[0].cls += ' navbar-right';
5373         }
5374         
5375         
5376         
5377         
5378         return cfg;
5379     
5380         
5381     }
5382     
5383     
5384     
5385 });
5386
5387
5388
5389  
5390
5391  
5392        /*
5393  * - LGPL
5394  *
5395  * navbar
5396  * navbar-fixed-top
5397  * navbar-expand-md  fixed-top 
5398  */
5399
5400 /**
5401  * @class Roo.bootstrap.NavHeaderbar
5402  * @extends Roo.bootstrap.NavSimplebar
5403  * Bootstrap Sidebar class
5404  *
5405  * @cfg {String} brand what is brand
5406  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5407  * @cfg {String} brand_href href of the brand
5408  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5409  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5410  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5411  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5412  * 
5413  * @constructor
5414  * Create a new Sidebar
5415  * @param {Object} config The config object
5416  */
5417
5418
5419 Roo.bootstrap.NavHeaderbar = function(config){
5420     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5421       
5422 };
5423
5424 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5425     
5426     position: '',
5427     brand: '',
5428     brand_href: false,
5429     srButton : true,
5430     autohide : false,
5431     desktopCenter : false,
5432    
5433     
5434     getAutoCreate : function(){
5435         
5436         var   cfg = {
5437             tag: this.nav || 'nav',
5438             cls: 'navbar navbar-expand-md',
5439             role: 'navigation',
5440             cn: []
5441         };
5442         
5443         var cn = cfg.cn;
5444         if (this.desktopCenter) {
5445             cn.push({cls : 'container', cn : []});
5446             cn = cn[0].cn;
5447         }
5448         
5449         if(this.srButton){
5450             var btn = {
5451                 tag: 'button',
5452                 type: 'button',
5453                 cls: 'navbar-toggle navbar-toggler',
5454                 'data-toggle': 'collapse',
5455                 cn: [
5456                     {
5457                         tag: 'span',
5458                         cls: 'sr-only',
5459                         html: 'Toggle navigation'
5460                     },
5461                     {
5462                         tag: 'span',
5463                         cls: 'icon-bar navbar-toggler-icon'
5464                     },
5465                     {
5466                         tag: 'span',
5467                         cls: 'icon-bar'
5468                     },
5469                     {
5470                         tag: 'span',
5471                         cls: 'icon-bar'
5472                     }
5473                 ]
5474             };
5475             
5476             cn.push( Roo.bootstrap.version == 4 ? btn : {
5477                 tag: 'div',
5478                 cls: 'navbar-header',
5479                 cn: [
5480                     btn
5481                 ]
5482             });
5483         }
5484         
5485         cn.push({
5486             tag: 'div',
5487             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5488             cn : []
5489         });
5490         
5491         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5492         
5493         if (['light','white'].indexOf(this.weight) > -1) {
5494             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5495         }
5496         cfg.cls += ' bg-' + this.weight;
5497         
5498         
5499         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5500             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5501             
5502             // tag can override this..
5503             
5504             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5505         }
5506         
5507         if (this.brand !== '') {
5508             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5509             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5510                 tag: 'a',
5511                 href: this.brand_href ? this.brand_href : '#',
5512                 cls: 'navbar-brand',
5513                 cn: [
5514                 this.brand
5515                 ]
5516             });
5517         }
5518         
5519         if(this.main){
5520             cfg.cls += ' main-nav';
5521         }
5522         
5523         
5524         return cfg;
5525
5526         
5527     },
5528     getHeaderChildContainer : function()
5529     {
5530         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5531             return this.el.select('.navbar-header',true).first();
5532         }
5533         
5534         return this.getChildContainer();
5535     },
5536     
5537     getChildContainer : function()
5538     {
5539          
5540         return this.el.select('.roo-navbar-collapse',true).first();
5541          
5542         
5543     },
5544     
5545     initEvents : function()
5546     {
5547         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5548         
5549         if (this.autohide) {
5550             
5551             var prevScroll = 0;
5552             var ft = this.el;
5553             
5554             Roo.get(document).on('scroll',function(e) {
5555                 var ns = Roo.get(document).getScroll().top;
5556                 var os = prevScroll;
5557                 prevScroll = ns;
5558                 
5559                 if(ns > os){
5560                     ft.removeClass('slideDown');
5561                     ft.addClass('slideUp');
5562                     return;
5563                 }
5564                 ft.removeClass('slideUp');
5565                 ft.addClass('slideDown');
5566                  
5567               
5568           },this);
5569         }
5570     }    
5571     
5572 });
5573
5574
5575
5576  
5577
5578  /*
5579  * - LGPL
5580  *
5581  * navbar
5582  * 
5583  */
5584
5585 /**
5586  * @class Roo.bootstrap.NavSidebar
5587  * @extends Roo.bootstrap.Navbar
5588  * Bootstrap Sidebar class
5589  * 
5590  * @constructor
5591  * Create a new Sidebar
5592  * @param {Object} config The config object
5593  */
5594
5595
5596 Roo.bootstrap.NavSidebar = function(config){
5597     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5598 };
5599
5600 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5601     
5602     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5603     
5604     getAutoCreate : function(){
5605         
5606         
5607         return  {
5608             tag: 'div',
5609             cls: 'sidebar sidebar-nav'
5610         };
5611     
5612         
5613     }
5614     
5615     
5616     
5617 });
5618
5619
5620
5621  
5622
5623  /*
5624  * - LGPL
5625  *
5626  * nav group
5627  * 
5628  */
5629
5630 /**
5631  * @class Roo.bootstrap.NavGroup
5632  * @extends Roo.bootstrap.Component
5633  * Bootstrap NavGroup class
5634  * @cfg {String} align (left|right)
5635  * @cfg {Boolean} inverse
5636  * @cfg {String} type (nav|pills|tab) default nav
5637  * @cfg {String} navId - reference Id for navbar.
5638
5639  * 
5640  * @constructor
5641  * Create a new nav group
5642  * @param {Object} config The config object
5643  */
5644
5645 Roo.bootstrap.NavGroup = function(config){
5646     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5647     this.navItems = [];
5648    
5649     Roo.bootstrap.NavGroup.register(this);
5650      this.addEvents({
5651         /**
5652              * @event changed
5653              * Fires when the active item changes
5654              * @param {Roo.bootstrap.NavGroup} this
5655              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5656              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5657          */
5658         'changed': true
5659      });
5660     
5661 };
5662
5663 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5664     
5665     align: '',
5666     inverse: false,
5667     form: false,
5668     type: 'nav',
5669     navId : '',
5670     // private
5671     
5672     navItems : false, 
5673     
5674     getAutoCreate : function()
5675     {
5676         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5677         
5678         cfg = {
5679             tag : 'ul',
5680             cls: 'nav' 
5681         };
5682         if (Roo.bootstrap.version == 4) {
5683             if (['tabs','pills'].indexOf(this.type) != -1) {
5684                 cfg.cls += ' nav-' + this.type; 
5685             } else {
5686                 // trying to remove so header bar can right align top?
5687                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5688                     // do not use on header bar... 
5689                     cfg.cls += ' navbar-nav';
5690                 }
5691             }
5692             
5693         } else {
5694             if (['tabs','pills'].indexOf(this.type) != -1) {
5695                 cfg.cls += ' nav-' + this.type
5696             } else {
5697                 if (this.type !== 'nav') {
5698                     Roo.log('nav type must be nav/tabs/pills')
5699                 }
5700                 cfg.cls += ' navbar-nav'
5701             }
5702         }
5703         
5704         if (this.parent() && this.parent().sidebar) {
5705             cfg = {
5706                 tag: 'ul',
5707                 cls: 'dashboard-menu sidebar-menu'
5708             };
5709             
5710             return cfg;
5711         }
5712         
5713         if (this.form === true) {
5714             cfg = {
5715                 tag: 'form',
5716                 cls: 'navbar-form form-inline'
5717             };
5718             //nav navbar-right ml-md-auto
5719             if (this.align === 'right') {
5720                 cfg.cls += ' navbar-right ml-md-auto';
5721             } else {
5722                 cfg.cls += ' navbar-left';
5723             }
5724         }
5725         
5726         if (this.align === 'right') {
5727             cfg.cls += ' navbar-right ml-md-auto';
5728         } else {
5729             cfg.cls += ' mr-auto';
5730         }
5731         
5732         if (this.inverse) {
5733             cfg.cls += ' navbar-inverse';
5734             
5735         }
5736         
5737         
5738         return cfg;
5739     },
5740     /**
5741     * sets the active Navigation item
5742     * @param {Roo.bootstrap.NavItem} the new current navitem
5743     */
5744     setActiveItem : function(item)
5745     {
5746         var prev = false;
5747         Roo.each(this.navItems, function(v){
5748             if (v == item) {
5749                 return ;
5750             }
5751             if (v.isActive()) {
5752                 v.setActive(false, true);
5753                 prev = v;
5754                 
5755             }
5756             
5757         });
5758
5759         item.setActive(true, true);
5760         this.fireEvent('changed', this, item, prev);
5761         
5762         
5763     },
5764     /**
5765     * gets the active Navigation item
5766     * @return {Roo.bootstrap.NavItem} the current navitem
5767     */
5768     getActive : function()
5769     {
5770         
5771         var prev = false;
5772         Roo.each(this.navItems, function(v){
5773             
5774             if (v.isActive()) {
5775                 prev = v;
5776                 
5777             }
5778             
5779         });
5780         return prev;
5781     },
5782     
5783     indexOfNav : function()
5784     {
5785         
5786         var prev = false;
5787         Roo.each(this.navItems, function(v,i){
5788             
5789             if (v.isActive()) {
5790                 prev = i;
5791                 
5792             }
5793             
5794         });
5795         return prev;
5796     },
5797     /**
5798     * adds a Navigation item
5799     * @param {Roo.bootstrap.NavItem} the navitem to add
5800     */
5801     addItem : function(cfg)
5802     {
5803         if (this.form && Roo.bootstrap.version == 4) {
5804             cfg.tag = 'div';
5805         }
5806         var cn = new Roo.bootstrap.NavItem(cfg);
5807         this.register(cn);
5808         cn.parentId = this.id;
5809         cn.onRender(this.el, null);
5810         return cn;
5811     },
5812     /**
5813     * register a Navigation item
5814     * @param {Roo.bootstrap.NavItem} the navitem to add
5815     */
5816     register : function(item)
5817     {
5818         this.navItems.push( item);
5819         item.navId = this.navId;
5820     
5821     },
5822     
5823     /**
5824     * clear all the Navigation item
5825     */
5826    
5827     clearAll : function()
5828     {
5829         this.navItems = [];
5830         this.el.dom.innerHTML = '';
5831     },
5832     
5833     getNavItem: function(tabId)
5834     {
5835         var ret = false;
5836         Roo.each(this.navItems, function(e) {
5837             if (e.tabId == tabId) {
5838                ret =  e;
5839                return false;
5840             }
5841             return true;
5842             
5843         });
5844         return ret;
5845     },
5846     
5847     setActiveNext : function()
5848     {
5849         var i = this.indexOfNav(this.getActive());
5850         if (i > this.navItems.length) {
5851             return;
5852         }
5853         this.setActiveItem(this.navItems[i+1]);
5854     },
5855     setActivePrev : function()
5856     {
5857         var i = this.indexOfNav(this.getActive());
5858         if (i  < 1) {
5859             return;
5860         }
5861         this.setActiveItem(this.navItems[i-1]);
5862     },
5863     clearWasActive : function(except) {
5864         Roo.each(this.navItems, function(e) {
5865             if (e.tabId != except.tabId && e.was_active) {
5866                e.was_active = false;
5867                return false;
5868             }
5869             return true;
5870             
5871         });
5872     },
5873     getWasActive : function ()
5874     {
5875         var r = false;
5876         Roo.each(this.navItems, function(e) {
5877             if (e.was_active) {
5878                r = e;
5879                return false;
5880             }
5881             return true;
5882             
5883         });
5884         return r;
5885     }
5886     
5887     
5888 });
5889
5890  
5891 Roo.apply(Roo.bootstrap.NavGroup, {
5892     
5893     groups: {},
5894      /**
5895     * register a Navigation Group
5896     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5897     */
5898     register : function(navgrp)
5899     {
5900         this.groups[navgrp.navId] = navgrp;
5901         
5902     },
5903     /**
5904     * fetch a Navigation Group based on the navigation ID
5905     * @param {string} the navgroup to add
5906     * @returns {Roo.bootstrap.NavGroup} the navgroup 
5907     */
5908     get: function(navId) {
5909         if (typeof(this.groups[navId]) == 'undefined') {
5910             return false;
5911             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
5912         }
5913         return this.groups[navId] ;
5914     }
5915     
5916     
5917     
5918 });
5919
5920  /*
5921  * - LGPL
5922  *
5923  * row
5924  * 
5925  */
5926
5927 /**
5928  * @class Roo.bootstrap.NavItem
5929  * @extends Roo.bootstrap.Component
5930  * Bootstrap Navbar.NavItem class
5931  * @cfg {String} href  link to
5932  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
5933
5934  * @cfg {String} html content of button
5935  * @cfg {String} badge text inside badge
5936  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
5937  * @cfg {String} glyphicon DEPRICATED - use fa
5938  * @cfg {String} icon DEPRICATED - use fa
5939  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
5940  * @cfg {Boolean} active Is item active
5941  * @cfg {Boolean} disabled Is item disabled
5942  
5943  * @cfg {Boolean} preventDefault (true | false) default false
5944  * @cfg {String} tabId the tab that this item activates.
5945  * @cfg {String} tagtype (a|span) render as a href or span?
5946  * @cfg {Boolean} animateRef (true|false) link to element default false  
5947   
5948  * @constructor
5949  * Create a new Navbar Item
5950  * @param {Object} config The config object
5951  */
5952 Roo.bootstrap.NavItem = function(config){
5953     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
5954     this.addEvents({
5955         // raw events
5956         /**
5957          * @event click
5958          * The raw click event for the entire grid.
5959          * @param {Roo.EventObject} e
5960          */
5961         "click" : true,
5962          /**
5963             * @event changed
5964             * Fires when the active item active state changes
5965             * @param {Roo.bootstrap.NavItem} this
5966             * @param {boolean} state the new state
5967              
5968          */
5969         'changed': true,
5970         /**
5971             * @event scrollto
5972             * Fires when scroll to element
5973             * @param {Roo.bootstrap.NavItem} this
5974             * @param {Object} options
5975             * @param {Roo.EventObject} e
5976              
5977          */
5978         'scrollto': true
5979     });
5980    
5981 };
5982
5983 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
5984     
5985     href: false,
5986     html: '',
5987     badge: '',
5988     icon: false,
5989     fa : false,
5990     glyphicon: false,
5991     active: false,
5992     preventDefault : false,
5993     tabId : false,
5994     tagtype : 'a',
5995     tag: 'li',
5996     disabled : false,
5997     animateRef : false,
5998     was_active : false,
5999     button_weight : '',
6000     button_outline : false,
6001     
6002     navLink: false,
6003     
6004     getAutoCreate : function(){
6005          
6006         var cfg = {
6007             tag: this.tag,
6008             cls: 'nav-item'
6009         };
6010         
6011         if (this.active) {
6012             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6013         }
6014         if (this.disabled) {
6015             cfg.cls += ' disabled';
6016         }
6017         
6018         // BS4 only?
6019         if (this.button_weight.length) {
6020             cfg.tag = this.href ? 'a' : 'button';
6021             cfg.html = this.html || '';
6022             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6023             if (this.href) {
6024                 cfg.href = this.href;
6025             }
6026             if (this.fa) {
6027                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6028             }
6029             
6030             // menu .. should add dropdown-menu class - so no need for carat..
6031             
6032             if (this.badge !== '') {
6033                  
6034                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6035             }
6036             return cfg;
6037         }
6038         
6039         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6040             cfg.cn = [
6041                 {
6042                     tag: this.tagtype,
6043                     href : this.href || "#",
6044                     html: this.html || ''
6045                 }
6046             ];
6047             if (this.tagtype == 'a') {
6048                 cfg.cn[0].cls = 'nav-link';
6049             }
6050             if (this.icon) {
6051                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6052             }
6053             if (this.fa) {
6054                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6055             }
6056             if(this.glyphicon) {
6057                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6058             }
6059             
6060             if (this.menu) {
6061                 
6062                 cfg.cn[0].html += " <span class='caret'></span>";
6063              
6064             }
6065             
6066             if (this.badge !== '') {
6067                  
6068                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6069             }
6070         }
6071         
6072         
6073         
6074         return cfg;
6075     },
6076     onRender : function(ct, position)
6077     {
6078        // Roo.log("Call onRender: " + this.xtype);
6079         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6080             this.tag = 'div';
6081         }
6082         
6083         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6084         this.navLink = this.el.select('.nav-link',true).first();
6085         return ret;
6086     },
6087       
6088     
6089     initEvents: function() 
6090     {
6091         if (typeof (this.menu) != 'undefined') {
6092             this.menu.parentType = this.xtype;
6093             this.menu.triggerEl = this.el;
6094             this.menu = this.addxtype(Roo.apply({}, this.menu));
6095         }
6096         
6097         this.el.select('a',true).on('click', this.onClick, this);
6098         
6099         if(this.tagtype == 'span'){
6100             this.el.select('span',true).on('click', this.onClick, this);
6101         }
6102        
6103         // at this point parent should be available..
6104         this.parent().register(this);
6105     },
6106     
6107     onClick : function(e)
6108     {
6109         if (e.getTarget('.dropdown-menu-item')) {
6110             // did you click on a menu itemm.... - then don't trigger onclick..
6111             return;
6112         }
6113         
6114         if(
6115                 this.preventDefault || 
6116                 this.href == '#' 
6117         ){
6118             Roo.log("NavItem - prevent Default?");
6119             e.preventDefault();
6120         }
6121         
6122         if (this.disabled) {
6123             return;
6124         }
6125         
6126         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6127         if (tg && tg.transition) {
6128             Roo.log("waiting for the transitionend");
6129             return;
6130         }
6131         
6132         
6133         
6134         //Roo.log("fire event clicked");
6135         if(this.fireEvent('click', this, e) === false){
6136             return;
6137         };
6138         
6139         if(this.tagtype == 'span'){
6140             return;
6141         }
6142         
6143         //Roo.log(this.href);
6144         var ael = this.el.select('a',true).first();
6145         //Roo.log(ael);
6146         
6147         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6148             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6149             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6150                 return; // ignore... - it's a 'hash' to another page.
6151             }
6152             Roo.log("NavItem - prevent Default?");
6153             e.preventDefault();
6154             this.scrollToElement(e);
6155         }
6156         
6157         
6158         var p =  this.parent();
6159    
6160         if (['tabs','pills'].indexOf(p.type)!==-1) {
6161             if (typeof(p.setActiveItem) !== 'undefined') {
6162                 p.setActiveItem(this);
6163             }
6164         }
6165         
6166         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6167         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6168             // remove the collapsed menu expand...
6169             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6170         }
6171     },
6172     
6173     isActive: function () {
6174         return this.active
6175     },
6176     setActive : function(state, fire, is_was_active)
6177     {
6178         if (this.active && !state && this.navId) {
6179             this.was_active = true;
6180             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6181             if (nv) {
6182                 nv.clearWasActive(this);
6183             }
6184             
6185         }
6186         this.active = state;
6187         
6188         if (!state ) {
6189             this.el.removeClass('active');
6190             this.navLink ? this.navLink.removeClass('active') : false;
6191         } else if (!this.el.hasClass('active')) {
6192             
6193             this.el.addClass('active');
6194             if (Roo.bootstrap.version == 4 && this.navLink ) {
6195                 this.navLink.addClass('active');
6196             }
6197             
6198         }
6199         if (fire) {
6200             this.fireEvent('changed', this, state);
6201         }
6202         
6203         // show a panel if it's registered and related..
6204         
6205         if (!this.navId || !this.tabId || !state || is_was_active) {
6206             return;
6207         }
6208         
6209         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6210         if (!tg) {
6211             return;
6212         }
6213         var pan = tg.getPanelByName(this.tabId);
6214         if (!pan) {
6215             return;
6216         }
6217         // if we can not flip to new panel - go back to old nav highlight..
6218         if (false == tg.showPanel(pan)) {
6219             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6220             if (nv) {
6221                 var onav = nv.getWasActive();
6222                 if (onav) {
6223                     onav.setActive(true, false, true);
6224                 }
6225             }
6226             
6227         }
6228         
6229         
6230         
6231     },
6232      // this should not be here...
6233     setDisabled : function(state)
6234     {
6235         this.disabled = state;
6236         if (!state ) {
6237             this.el.removeClass('disabled');
6238         } else if (!this.el.hasClass('disabled')) {
6239             this.el.addClass('disabled');
6240         }
6241         
6242     },
6243     
6244     /**
6245      * Fetch the element to display the tooltip on.
6246      * @return {Roo.Element} defaults to this.el
6247      */
6248     tooltipEl : function()
6249     {
6250         return this.el.select('' + this.tagtype + '', true).first();
6251     },
6252     
6253     scrollToElement : function(e)
6254     {
6255         var c = document.body;
6256         
6257         /*
6258          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6259          */
6260         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6261             c = document.documentElement;
6262         }
6263         
6264         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6265         
6266         if(!target){
6267             return;
6268         }
6269
6270         var o = target.calcOffsetsTo(c);
6271         
6272         var options = {
6273             target : target,
6274             value : o[1]
6275         };
6276         
6277         this.fireEvent('scrollto', this, options, e);
6278         
6279         Roo.get(c).scrollTo('top', options.value, true);
6280         
6281         return;
6282     }
6283 });
6284  
6285
6286  /*
6287  * - LGPL
6288  *
6289  * sidebar item
6290  *
6291  *  li
6292  *    <span> icon </span>
6293  *    <span> text </span>
6294  *    <span>badge </span>
6295  */
6296
6297 /**
6298  * @class Roo.bootstrap.NavSidebarItem
6299  * @extends Roo.bootstrap.NavItem
6300  * Bootstrap Navbar.NavSidebarItem class
6301  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6302  * {Boolean} open is the menu open
6303  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6304  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6305  * {String} buttonSize (sm|md|lg)the extra classes for the button
6306  * {Boolean} showArrow show arrow next to the text (default true)
6307  * @constructor
6308  * Create a new Navbar Button
6309  * @param {Object} config The config object
6310  */
6311 Roo.bootstrap.NavSidebarItem = function(config){
6312     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6313     this.addEvents({
6314         // raw events
6315         /**
6316          * @event click
6317          * The raw click event for the entire grid.
6318          * @param {Roo.EventObject} e
6319          */
6320         "click" : true,
6321          /**
6322             * @event changed
6323             * Fires when the active item active state changes
6324             * @param {Roo.bootstrap.NavSidebarItem} this
6325             * @param {boolean} state the new state
6326              
6327          */
6328         'changed': true
6329     });
6330    
6331 };
6332
6333 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6334     
6335     badgeWeight : 'default',
6336     
6337     open: false,
6338     
6339     buttonView : false,
6340     
6341     buttonWeight : 'default',
6342     
6343     buttonSize : 'md',
6344     
6345     showArrow : true,
6346     
6347     getAutoCreate : function(){
6348         
6349         
6350         var a = {
6351                 tag: 'a',
6352                 href : this.href || '#',
6353                 cls: '',
6354                 html : '',
6355                 cn : []
6356         };
6357         
6358         if(this.buttonView){
6359             a = {
6360                 tag: 'button',
6361                 href : this.href || '#',
6362                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6363                 html : this.html,
6364                 cn : []
6365             };
6366         }
6367         
6368         var cfg = {
6369             tag: 'li',
6370             cls: '',
6371             cn: [ a ]
6372         };
6373         
6374         if (this.active) {
6375             cfg.cls += ' active';
6376         }
6377         
6378         if (this.disabled) {
6379             cfg.cls += ' disabled';
6380         }
6381         if (this.open) {
6382             cfg.cls += ' open x-open';
6383         }
6384         // left icon..
6385         if (this.glyphicon || this.icon) {
6386             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6387             a.cn.push({ tag : 'i', cls : c }) ;
6388         }
6389         
6390         if(!this.buttonView){
6391             var span = {
6392                 tag: 'span',
6393                 html : this.html || ''
6394             };
6395
6396             a.cn.push(span);
6397             
6398         }
6399         
6400         if (this.badge !== '') {
6401             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6402         }
6403         
6404         if (this.menu) {
6405             
6406             if(this.showArrow){
6407                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6408             }
6409             
6410             a.cls += ' dropdown-toggle treeview' ;
6411         }
6412         
6413         return cfg;
6414     },
6415     
6416     initEvents : function()
6417     { 
6418         if (typeof (this.menu) != 'undefined') {
6419             this.menu.parentType = this.xtype;
6420             this.menu.triggerEl = this.el;
6421             this.menu = this.addxtype(Roo.apply({}, this.menu));
6422         }
6423         
6424         this.el.on('click', this.onClick, this);
6425         
6426         if(this.badge !== ''){
6427             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6428         }
6429         
6430     },
6431     
6432     onClick : function(e)
6433     {
6434         if(this.disabled){
6435             e.preventDefault();
6436             return;
6437         }
6438         
6439         if(this.preventDefault){
6440             e.preventDefault();
6441         }
6442         
6443         this.fireEvent('click', this, e);
6444     },
6445     
6446     disable : function()
6447     {
6448         this.setDisabled(true);
6449     },
6450     
6451     enable : function()
6452     {
6453         this.setDisabled(false);
6454     },
6455     
6456     setDisabled : function(state)
6457     {
6458         if(this.disabled == state){
6459             return;
6460         }
6461         
6462         this.disabled = state;
6463         
6464         if (state) {
6465             this.el.addClass('disabled');
6466             return;
6467         }
6468         
6469         this.el.removeClass('disabled');
6470         
6471         return;
6472     },
6473     
6474     setActive : function(state)
6475     {
6476         if(this.active == state){
6477             return;
6478         }
6479         
6480         this.active = state;
6481         
6482         if (state) {
6483             this.el.addClass('active');
6484             return;
6485         }
6486         
6487         this.el.removeClass('active');
6488         
6489         return;
6490     },
6491     
6492     isActive: function () 
6493     {
6494         return this.active;
6495     },
6496     
6497     setBadge : function(str)
6498     {
6499         if(!this.badgeEl){
6500             return;
6501         }
6502         
6503         this.badgeEl.dom.innerHTML = str;
6504     }
6505     
6506    
6507      
6508  
6509 });
6510  
6511
6512  /*
6513  * - LGPL
6514  *
6515  * row
6516  * 
6517  */
6518
6519 /**
6520  * @class Roo.bootstrap.Row
6521  * @extends Roo.bootstrap.Component
6522  * Bootstrap Row class (contains columns...)
6523  * 
6524  * @constructor
6525  * Create a new Row
6526  * @param {Object} config The config object
6527  */
6528
6529 Roo.bootstrap.Row = function(config){
6530     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6531 };
6532
6533 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6534     
6535     getAutoCreate : function(){
6536        return {
6537             cls: 'row clearfix'
6538        };
6539     }
6540     
6541     
6542 });
6543
6544  
6545
6546  /*
6547  * - LGPL
6548  *
6549  * pagination
6550  * 
6551  */
6552
6553 /**
6554  * @class Roo.bootstrap.Pagination
6555  * @extends Roo.bootstrap.Component
6556  * Bootstrap Pagination class
6557  * @cfg {String} size xs | sm | md | lg
6558  * @cfg {Boolean} inverse false | true
6559  * 
6560  * @constructor
6561  * Create a new Pagination
6562  * @param {Object} config The config object
6563  */
6564
6565 Roo.bootstrap.Pagination = function(config){
6566     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6567 };
6568
6569 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6570     
6571     cls: false,
6572     size: false,
6573     inverse: false,
6574     
6575     getAutoCreate : function(){
6576         var cfg = {
6577             tag: 'ul',
6578                 cls: 'pagination'
6579         };
6580         if (this.inverse) {
6581             cfg.cls += ' inverse';
6582         }
6583         if (this.html) {
6584             cfg.html=this.html;
6585         }
6586         if (this.cls) {
6587             cfg.cls += " " + this.cls;
6588         }
6589         return cfg;
6590     }
6591    
6592 });
6593
6594  
6595
6596  /*
6597  * - LGPL
6598  *
6599  * Pagination item
6600  * 
6601  */
6602
6603
6604 /**
6605  * @class Roo.bootstrap.PaginationItem
6606  * @extends Roo.bootstrap.Component
6607  * Bootstrap PaginationItem class
6608  * @cfg {String} html text
6609  * @cfg {String} href the link
6610  * @cfg {Boolean} preventDefault (true | false) default true
6611  * @cfg {Boolean} active (true | false) default false
6612  * @cfg {Boolean} disabled default false
6613  * 
6614  * 
6615  * @constructor
6616  * Create a new PaginationItem
6617  * @param {Object} config The config object
6618  */
6619
6620
6621 Roo.bootstrap.PaginationItem = function(config){
6622     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6623     this.addEvents({
6624         // raw events
6625         /**
6626          * @event click
6627          * The raw click event for the entire grid.
6628          * @param {Roo.EventObject} e
6629          */
6630         "click" : true
6631     });
6632 };
6633
6634 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6635     
6636     href : false,
6637     html : false,
6638     preventDefault: true,
6639     active : false,
6640     cls : false,
6641     disabled: false,
6642     
6643     getAutoCreate : function(){
6644         var cfg= {
6645             tag: 'li',
6646             cn: [
6647                 {
6648                     tag : 'a',
6649                     href : this.href ? this.href : '#',
6650                     html : this.html ? this.html : ''
6651                 }
6652             ]
6653         };
6654         
6655         if(this.cls){
6656             cfg.cls = this.cls;
6657         }
6658         
6659         if(this.disabled){
6660             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6661         }
6662         
6663         if(this.active){
6664             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6665         }
6666         
6667         return cfg;
6668     },
6669     
6670     initEvents: function() {
6671         
6672         this.el.on('click', this.onClick, this);
6673         
6674     },
6675     onClick : function(e)
6676     {
6677         Roo.log('PaginationItem on click ');
6678         if(this.preventDefault){
6679             e.preventDefault();
6680         }
6681         
6682         if(this.disabled){
6683             return;
6684         }
6685         
6686         this.fireEvent('click', this, e);
6687     }
6688    
6689 });
6690
6691  
6692
6693  /*
6694  * - LGPL
6695  *
6696  * slider
6697  * 
6698  */
6699
6700
6701 /**
6702  * @class Roo.bootstrap.Slider
6703  * @extends Roo.bootstrap.Component
6704  * Bootstrap Slider class
6705  *    
6706  * @constructor
6707  * Create a new Slider
6708  * @param {Object} config The config object
6709  */
6710
6711 Roo.bootstrap.Slider = function(config){
6712     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6713 };
6714
6715 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6716     
6717     getAutoCreate : function(){
6718         
6719         var cfg = {
6720             tag: 'div',
6721             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6722             cn: [
6723                 {
6724                     tag: 'a',
6725                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6726                 }
6727             ]
6728         };
6729         
6730         return cfg;
6731     }
6732    
6733 });
6734
6735  /*
6736  * Based on:
6737  * Ext JS Library 1.1.1
6738  * Copyright(c) 2006-2007, Ext JS, LLC.
6739  *
6740  * Originally Released Under LGPL - original licence link has changed is not relivant.
6741  *
6742  * Fork - LGPL
6743  * <script type="text/javascript">
6744  */
6745  
6746
6747 /**
6748  * @class Roo.grid.ColumnModel
6749  * @extends Roo.util.Observable
6750  * This is the default implementation of a ColumnModel used by the Grid. It defines
6751  * the columns in the grid.
6752  * <br>Usage:<br>
6753  <pre><code>
6754  var colModel = new Roo.grid.ColumnModel([
6755         {header: "Ticker", width: 60, sortable: true, locked: true},
6756         {header: "Company Name", width: 150, sortable: true},
6757         {header: "Market Cap.", width: 100, sortable: true},
6758         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6759         {header: "Employees", width: 100, sortable: true, resizable: false}
6760  ]);
6761  </code></pre>
6762  * <p>
6763  
6764  * The config options listed for this class are options which may appear in each
6765  * individual column definition.
6766  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6767  * @constructor
6768  * @param {Object} config An Array of column config objects. See this class's
6769  * config objects for details.
6770 */
6771 Roo.grid.ColumnModel = function(config){
6772         /**
6773      * The config passed into the constructor
6774      */
6775     this.config = config;
6776     this.lookup = {};
6777
6778     // if no id, create one
6779     // if the column does not have a dataIndex mapping,
6780     // map it to the order it is in the config
6781     for(var i = 0, len = config.length; i < len; i++){
6782         var c = config[i];
6783         if(typeof c.dataIndex == "undefined"){
6784             c.dataIndex = i;
6785         }
6786         if(typeof c.renderer == "string"){
6787             c.renderer = Roo.util.Format[c.renderer];
6788         }
6789         if(typeof c.id == "undefined"){
6790             c.id = Roo.id();
6791         }
6792         if(c.editor && c.editor.xtype){
6793             c.editor  = Roo.factory(c.editor, Roo.grid);
6794         }
6795         if(c.editor && c.editor.isFormField){
6796             c.editor = new Roo.grid.GridEditor(c.editor);
6797         }
6798         this.lookup[c.id] = c;
6799     }
6800
6801     /**
6802      * The width of columns which have no width specified (defaults to 100)
6803      * @type Number
6804      */
6805     this.defaultWidth = 100;
6806
6807     /**
6808      * Default sortable of columns which have no sortable specified (defaults to false)
6809      * @type Boolean
6810      */
6811     this.defaultSortable = false;
6812
6813     this.addEvents({
6814         /**
6815              * @event widthchange
6816              * Fires when the width of a column changes.
6817              * @param {ColumnModel} this
6818              * @param {Number} columnIndex The column index
6819              * @param {Number} newWidth The new width
6820              */
6821             "widthchange": true,
6822         /**
6823              * @event headerchange
6824              * Fires when the text of a header changes.
6825              * @param {ColumnModel} this
6826              * @param {Number} columnIndex The column index
6827              * @param {Number} newText The new header text
6828              */
6829             "headerchange": true,
6830         /**
6831              * @event hiddenchange
6832              * Fires when a column is hidden or "unhidden".
6833              * @param {ColumnModel} this
6834              * @param {Number} columnIndex The column index
6835              * @param {Boolean} hidden true if hidden, false otherwise
6836              */
6837             "hiddenchange": true,
6838             /**
6839          * @event columnmoved
6840          * Fires when a column is moved.
6841          * @param {ColumnModel} this
6842          * @param {Number} oldIndex
6843          * @param {Number} newIndex
6844          */
6845         "columnmoved" : true,
6846         /**
6847          * @event columlockchange
6848          * Fires when a column's locked state is changed
6849          * @param {ColumnModel} this
6850          * @param {Number} colIndex
6851          * @param {Boolean} locked true if locked
6852          */
6853         "columnlockchange" : true
6854     });
6855     Roo.grid.ColumnModel.superclass.constructor.call(this);
6856 };
6857 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6858     /**
6859      * @cfg {String} header The header text to display in the Grid view.
6860      */
6861     /**
6862      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6863      * {@link Roo.data.Record} definition from which to draw the column's value. If not
6864      * specified, the column's index is used as an index into the Record's data Array.
6865      */
6866     /**
6867      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6868      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6869      */
6870     /**
6871      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6872      * Defaults to the value of the {@link #defaultSortable} property.
6873      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6874      */
6875     /**
6876      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
6877      */
6878     /**
6879      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
6880      */
6881     /**
6882      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6883      */
6884     /**
6885      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6886      */
6887     /**
6888      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6889      * given the cell's data value. See {@link #setRenderer}. If not specified, the
6890      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6891      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6892      */
6893        /**
6894      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
6895      */
6896     /**
6897      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
6898      */
6899     /**
6900      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
6901      */
6902     /**
6903      * @cfg {String} cursor (Optional)
6904      */
6905     /**
6906      * @cfg {String} tooltip (Optional)
6907      */
6908     /**
6909      * @cfg {Number} xs (Optional)
6910      */
6911     /**
6912      * @cfg {Number} sm (Optional)
6913      */
6914     /**
6915      * @cfg {Number} md (Optional)
6916      */
6917     /**
6918      * @cfg {Number} lg (Optional)
6919      */
6920     /**
6921      * Returns the id of the column at the specified index.
6922      * @param {Number} index The column index
6923      * @return {String} the id
6924      */
6925     getColumnId : function(index){
6926         return this.config[index].id;
6927     },
6928
6929     /**
6930      * Returns the column for a specified id.
6931      * @param {String} id The column id
6932      * @return {Object} the column
6933      */
6934     getColumnById : function(id){
6935         return this.lookup[id];
6936     },
6937
6938     
6939     /**
6940      * Returns the column for a specified dataIndex.
6941      * @param {String} dataIndex The column dataIndex
6942      * @return {Object|Boolean} the column or false if not found
6943      */
6944     getColumnByDataIndex: function(dataIndex){
6945         var index = this.findColumnIndex(dataIndex);
6946         return index > -1 ? this.config[index] : false;
6947     },
6948     
6949     /**
6950      * Returns the index for a specified column id.
6951      * @param {String} id The column id
6952      * @return {Number} the index, or -1 if not found
6953      */
6954     getIndexById : function(id){
6955         for(var i = 0, len = this.config.length; i < len; i++){
6956             if(this.config[i].id == id){
6957                 return i;
6958             }
6959         }
6960         return -1;
6961     },
6962     
6963     /**
6964      * Returns the index for a specified column dataIndex.
6965      * @param {String} dataIndex The column dataIndex
6966      * @return {Number} the index, or -1 if not found
6967      */
6968     
6969     findColumnIndex : function(dataIndex){
6970         for(var i = 0, len = this.config.length; i < len; i++){
6971             if(this.config[i].dataIndex == dataIndex){
6972                 return i;
6973             }
6974         }
6975         return -1;
6976     },
6977     
6978     
6979     moveColumn : function(oldIndex, newIndex){
6980         var c = this.config[oldIndex];
6981         this.config.splice(oldIndex, 1);
6982         this.config.splice(newIndex, 0, c);
6983         this.dataMap = null;
6984         this.fireEvent("columnmoved", this, oldIndex, newIndex);
6985     },
6986
6987     isLocked : function(colIndex){
6988         return this.config[colIndex].locked === true;
6989     },
6990
6991     setLocked : function(colIndex, value, suppressEvent){
6992         if(this.isLocked(colIndex) == value){
6993             return;
6994         }
6995         this.config[colIndex].locked = value;
6996         if(!suppressEvent){
6997             this.fireEvent("columnlockchange", this, colIndex, value);
6998         }
6999     },
7000
7001     getTotalLockedWidth : function(){
7002         var totalWidth = 0;
7003         for(var i = 0; i < this.config.length; i++){
7004             if(this.isLocked(i) && !this.isHidden(i)){
7005                 this.totalWidth += this.getColumnWidth(i);
7006             }
7007         }
7008         return totalWidth;
7009     },
7010
7011     getLockedCount : function(){
7012         for(var i = 0, len = this.config.length; i < len; i++){
7013             if(!this.isLocked(i)){
7014                 return i;
7015             }
7016         }
7017         
7018         return this.config.length;
7019     },
7020
7021     /**
7022      * Returns the number of columns.
7023      * @return {Number}
7024      */
7025     getColumnCount : function(visibleOnly){
7026         if(visibleOnly === true){
7027             var c = 0;
7028             for(var i = 0, len = this.config.length; i < len; i++){
7029                 if(!this.isHidden(i)){
7030                     c++;
7031                 }
7032             }
7033             return c;
7034         }
7035         return this.config.length;
7036     },
7037
7038     /**
7039      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7040      * @param {Function} fn
7041      * @param {Object} scope (optional)
7042      * @return {Array} result
7043      */
7044     getColumnsBy : function(fn, scope){
7045         var r = [];
7046         for(var i = 0, len = this.config.length; i < len; i++){
7047             var c = this.config[i];
7048             if(fn.call(scope||this, c, i) === true){
7049                 r[r.length] = c;
7050             }
7051         }
7052         return r;
7053     },
7054
7055     /**
7056      * Returns true if the specified column is sortable.
7057      * @param {Number} col The column index
7058      * @return {Boolean}
7059      */
7060     isSortable : function(col){
7061         if(typeof this.config[col].sortable == "undefined"){
7062             return this.defaultSortable;
7063         }
7064         return this.config[col].sortable;
7065     },
7066
7067     /**
7068      * Returns the rendering (formatting) function defined for the column.
7069      * @param {Number} col The column index.
7070      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7071      */
7072     getRenderer : function(col){
7073         if(!this.config[col].renderer){
7074             return Roo.grid.ColumnModel.defaultRenderer;
7075         }
7076         return this.config[col].renderer;
7077     },
7078
7079     /**
7080      * Sets the rendering (formatting) function for a column.
7081      * @param {Number} col The column index
7082      * @param {Function} fn The function to use to process the cell's raw data
7083      * to return HTML markup for the grid view. The render function is called with
7084      * the following parameters:<ul>
7085      * <li>Data value.</li>
7086      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7087      * <li>css A CSS style string to apply to the table cell.</li>
7088      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7089      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7090      * <li>Row index</li>
7091      * <li>Column index</li>
7092      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7093      */
7094     setRenderer : function(col, fn){
7095         this.config[col].renderer = fn;
7096     },
7097
7098     /**
7099      * Returns the width for the specified column.
7100      * @param {Number} col The column index
7101      * @return {Number}
7102      */
7103     getColumnWidth : function(col){
7104         return this.config[col].width * 1 || this.defaultWidth;
7105     },
7106
7107     /**
7108      * Sets the width for a column.
7109      * @param {Number} col The column index
7110      * @param {Number} width The new width
7111      */
7112     setColumnWidth : function(col, width, suppressEvent){
7113         this.config[col].width = width;
7114         this.totalWidth = null;
7115         if(!suppressEvent){
7116              this.fireEvent("widthchange", this, col, width);
7117         }
7118     },
7119
7120     /**
7121      * Returns the total width of all columns.
7122      * @param {Boolean} includeHidden True to include hidden column widths
7123      * @return {Number}
7124      */
7125     getTotalWidth : function(includeHidden){
7126         if(!this.totalWidth){
7127             this.totalWidth = 0;
7128             for(var i = 0, len = this.config.length; i < len; i++){
7129                 if(includeHidden || !this.isHidden(i)){
7130                     this.totalWidth += this.getColumnWidth(i);
7131                 }
7132             }
7133         }
7134         return this.totalWidth;
7135     },
7136
7137     /**
7138      * Returns the header for the specified column.
7139      * @param {Number} col The column index
7140      * @return {String}
7141      */
7142     getColumnHeader : function(col){
7143         return this.config[col].header;
7144     },
7145
7146     /**
7147      * Sets the header for a column.
7148      * @param {Number} col The column index
7149      * @param {String} header The new header
7150      */
7151     setColumnHeader : function(col, header){
7152         this.config[col].header = header;
7153         this.fireEvent("headerchange", this, col, header);
7154     },
7155
7156     /**
7157      * Returns the tooltip for the specified column.
7158      * @param {Number} col The column index
7159      * @return {String}
7160      */
7161     getColumnTooltip : function(col){
7162             return this.config[col].tooltip;
7163     },
7164     /**
7165      * Sets the tooltip for a column.
7166      * @param {Number} col The column index
7167      * @param {String} tooltip The new tooltip
7168      */
7169     setColumnTooltip : function(col, tooltip){
7170             this.config[col].tooltip = tooltip;
7171     },
7172
7173     /**
7174      * Returns the dataIndex for the specified column.
7175      * @param {Number} col The column index
7176      * @return {Number}
7177      */
7178     getDataIndex : function(col){
7179         return this.config[col].dataIndex;
7180     },
7181
7182     /**
7183      * Sets the dataIndex for a column.
7184      * @param {Number} col The column index
7185      * @param {Number} dataIndex The new dataIndex
7186      */
7187     setDataIndex : function(col, dataIndex){
7188         this.config[col].dataIndex = dataIndex;
7189     },
7190
7191     
7192     
7193     /**
7194      * Returns true if the cell is editable.
7195      * @param {Number} colIndex The column index
7196      * @param {Number} rowIndex The row index - this is nto actually used..?
7197      * @return {Boolean}
7198      */
7199     isCellEditable : function(colIndex, rowIndex){
7200         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7201     },
7202
7203     /**
7204      * Returns the editor defined for the cell/column.
7205      * return false or null to disable editing.
7206      * @param {Number} colIndex The column index
7207      * @param {Number} rowIndex The row index
7208      * @return {Object}
7209      */
7210     getCellEditor : function(colIndex, rowIndex){
7211         return this.config[colIndex].editor;
7212     },
7213
7214     /**
7215      * Sets if a column is editable.
7216      * @param {Number} col The column index
7217      * @param {Boolean} editable True if the column is editable
7218      */
7219     setEditable : function(col, editable){
7220         this.config[col].editable = editable;
7221     },
7222
7223
7224     /**
7225      * Returns true if the column is hidden.
7226      * @param {Number} colIndex The column index
7227      * @return {Boolean}
7228      */
7229     isHidden : function(colIndex){
7230         return this.config[colIndex].hidden;
7231     },
7232
7233
7234     /**
7235      * Returns true if the column width cannot be changed
7236      */
7237     isFixed : function(colIndex){
7238         return this.config[colIndex].fixed;
7239     },
7240
7241     /**
7242      * Returns true if the column can be resized
7243      * @return {Boolean}
7244      */
7245     isResizable : function(colIndex){
7246         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7247     },
7248     /**
7249      * Sets if a column is hidden.
7250      * @param {Number} colIndex The column index
7251      * @param {Boolean} hidden True if the column is hidden
7252      */
7253     setHidden : function(colIndex, hidden){
7254         this.config[colIndex].hidden = hidden;
7255         this.totalWidth = null;
7256         this.fireEvent("hiddenchange", this, colIndex, hidden);
7257     },
7258
7259     /**
7260      * Sets the editor for a column.
7261      * @param {Number} col The column index
7262      * @param {Object} editor The editor object
7263      */
7264     setEditor : function(col, editor){
7265         this.config[col].editor = editor;
7266     }
7267 });
7268
7269 Roo.grid.ColumnModel.defaultRenderer = function(value)
7270 {
7271     if(typeof value == "object") {
7272         return value;
7273     }
7274         if(typeof value == "string" && value.length < 1){
7275             return "&#160;";
7276         }
7277     
7278         return String.format("{0}", value);
7279 };
7280
7281 // Alias for backwards compatibility
7282 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7283 /*
7284  * Based on:
7285  * Ext JS Library 1.1.1
7286  * Copyright(c) 2006-2007, Ext JS, LLC.
7287  *
7288  * Originally Released Under LGPL - original licence link has changed is not relivant.
7289  *
7290  * Fork - LGPL
7291  * <script type="text/javascript">
7292  */
7293  
7294 /**
7295  * @class Roo.LoadMask
7296  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7297  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7298  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7299  * element's UpdateManager load indicator and will be destroyed after the initial load.
7300  * @constructor
7301  * Create a new LoadMask
7302  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7303  * @param {Object} config The config object
7304  */
7305 Roo.LoadMask = function(el, config){
7306     this.el = Roo.get(el);
7307     Roo.apply(this, config);
7308     if(this.store){
7309         this.store.on('beforeload', this.onBeforeLoad, this);
7310         this.store.on('load', this.onLoad, this);
7311         this.store.on('loadexception', this.onLoadException, this);
7312         this.removeMask = false;
7313     }else{
7314         var um = this.el.getUpdateManager();
7315         um.showLoadIndicator = false; // disable the default indicator
7316         um.on('beforeupdate', this.onBeforeLoad, this);
7317         um.on('update', this.onLoad, this);
7318         um.on('failure', this.onLoad, this);
7319         this.removeMask = true;
7320     }
7321 };
7322
7323 Roo.LoadMask.prototype = {
7324     /**
7325      * @cfg {Boolean} removeMask
7326      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7327      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7328      */
7329     /**
7330      * @cfg {String} msg
7331      * The text to display in a centered loading message box (defaults to 'Loading...')
7332      */
7333     msg : 'Loading...',
7334     /**
7335      * @cfg {String} msgCls
7336      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7337      */
7338     msgCls : 'x-mask-loading',
7339
7340     /**
7341      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7342      * @type Boolean
7343      */
7344     disabled: false,
7345
7346     /**
7347      * Disables the mask to prevent it from being displayed
7348      */
7349     disable : function(){
7350        this.disabled = true;
7351     },
7352
7353     /**
7354      * Enables the mask so that it can be displayed
7355      */
7356     enable : function(){
7357         this.disabled = false;
7358     },
7359     
7360     onLoadException : function()
7361     {
7362         Roo.log(arguments);
7363         
7364         if (typeof(arguments[3]) != 'undefined') {
7365             Roo.MessageBox.alert("Error loading",arguments[3]);
7366         } 
7367         /*
7368         try {
7369             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7370                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7371             }   
7372         } catch(e) {
7373             
7374         }
7375         */
7376     
7377         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7378     },
7379     // private
7380     onLoad : function()
7381     {
7382         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7383     },
7384
7385     // private
7386     onBeforeLoad : function(){
7387         if(!this.disabled){
7388             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7389         }
7390     },
7391
7392     // private
7393     destroy : function(){
7394         if(this.store){
7395             this.store.un('beforeload', this.onBeforeLoad, this);
7396             this.store.un('load', this.onLoad, this);
7397             this.store.un('loadexception', this.onLoadException, this);
7398         }else{
7399             var um = this.el.getUpdateManager();
7400             um.un('beforeupdate', this.onBeforeLoad, this);
7401             um.un('update', this.onLoad, this);
7402             um.un('failure', this.onLoad, this);
7403         }
7404     }
7405 };/*
7406  * - LGPL
7407  *
7408  * table
7409  * 
7410  */
7411
7412 /**
7413  * @class Roo.bootstrap.Table
7414  * @extends Roo.bootstrap.Component
7415  * Bootstrap Table class
7416  * @cfg {String} cls table class
7417  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7418  * @cfg {String} bgcolor Specifies the background color for a table
7419  * @cfg {Number} border Specifies whether the table cells should have borders or not
7420  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7421  * @cfg {Number} cellspacing Specifies the space between cells
7422  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7423  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7424  * @cfg {String} sortable Specifies that the table should be sortable
7425  * @cfg {String} summary Specifies a summary of the content of a table
7426  * @cfg {Number} width Specifies the width of a table
7427  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7428  * 
7429  * @cfg {boolean} striped Should the rows be alternative striped
7430  * @cfg {boolean} bordered Add borders to the table
7431  * @cfg {boolean} hover Add hover highlighting
7432  * @cfg {boolean} condensed Format condensed
7433  * @cfg {boolean} responsive Format condensed
7434  * @cfg {Boolean} loadMask (true|false) default false
7435  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7436  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7437  * @cfg {Boolean} rowSelection (true|false) default false
7438  * @cfg {Boolean} cellSelection (true|false) default false
7439  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7440  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7441  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7442  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7443  
7444  * 
7445  * @constructor
7446  * Create a new Table
7447  * @param {Object} config The config object
7448  */
7449
7450 Roo.bootstrap.Table = function(config){
7451     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7452     
7453   
7454     
7455     // BC...
7456     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7457     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7458     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7459     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7460     
7461     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7462     if (this.sm) {
7463         this.sm.grid = this;
7464         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7465         this.sm = this.selModel;
7466         this.sm.xmodule = this.xmodule || false;
7467     }
7468     
7469     if (this.cm && typeof(this.cm.config) == 'undefined') {
7470         this.colModel = new Roo.grid.ColumnModel(this.cm);
7471         this.cm = this.colModel;
7472         this.cm.xmodule = this.xmodule || false;
7473     }
7474     if (this.store) {
7475         this.store= Roo.factory(this.store, Roo.data);
7476         this.ds = this.store;
7477         this.ds.xmodule = this.xmodule || false;
7478          
7479     }
7480     if (this.footer && this.store) {
7481         this.footer.dataSource = this.ds;
7482         this.footer = Roo.factory(this.footer);
7483     }
7484     
7485     /** @private */
7486     this.addEvents({
7487         /**
7488          * @event cellclick
7489          * Fires when a cell is clicked
7490          * @param {Roo.bootstrap.Table} this
7491          * @param {Roo.Element} el
7492          * @param {Number} rowIndex
7493          * @param {Number} columnIndex
7494          * @param {Roo.EventObject} e
7495          */
7496         "cellclick" : true,
7497         /**
7498          * @event celldblclick
7499          * Fires when a cell is double clicked
7500          * @param {Roo.bootstrap.Table} this
7501          * @param {Roo.Element} el
7502          * @param {Number} rowIndex
7503          * @param {Number} columnIndex
7504          * @param {Roo.EventObject} e
7505          */
7506         "celldblclick" : true,
7507         /**
7508          * @event rowclick
7509          * Fires when a row is clicked
7510          * @param {Roo.bootstrap.Table} this
7511          * @param {Roo.Element} el
7512          * @param {Number} rowIndex
7513          * @param {Roo.EventObject} e
7514          */
7515         "rowclick" : true,
7516         /**
7517          * @event rowdblclick
7518          * Fires when a row is double clicked
7519          * @param {Roo.bootstrap.Table} this
7520          * @param {Roo.Element} el
7521          * @param {Number} rowIndex
7522          * @param {Roo.EventObject} e
7523          */
7524         "rowdblclick" : true,
7525         /**
7526          * @event mouseover
7527          * Fires when a mouseover occur
7528          * @param {Roo.bootstrap.Table} this
7529          * @param {Roo.Element} el
7530          * @param {Number} rowIndex
7531          * @param {Number} columnIndex
7532          * @param {Roo.EventObject} e
7533          */
7534         "mouseover" : true,
7535         /**
7536          * @event mouseout
7537          * Fires when a mouseout occur
7538          * @param {Roo.bootstrap.Table} this
7539          * @param {Roo.Element} el
7540          * @param {Number} rowIndex
7541          * @param {Number} columnIndex
7542          * @param {Roo.EventObject} e
7543          */
7544         "mouseout" : true,
7545         /**
7546          * @event rowclass
7547          * Fires when a row is rendered, so you can change add a style to it.
7548          * @param {Roo.bootstrap.Table} this
7549          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7550          */
7551         'rowclass' : true,
7552           /**
7553          * @event rowsrendered
7554          * Fires when all the  rows have been rendered
7555          * @param {Roo.bootstrap.Table} this
7556          */
7557         'rowsrendered' : true,
7558         /**
7559          * @event contextmenu
7560          * The raw contextmenu event for the entire grid.
7561          * @param {Roo.EventObject} e
7562          */
7563         "contextmenu" : true,
7564         /**
7565          * @event rowcontextmenu
7566          * Fires when a row is right clicked
7567          * @param {Roo.bootstrap.Table} this
7568          * @param {Number} rowIndex
7569          * @param {Roo.EventObject} e
7570          */
7571         "rowcontextmenu" : true,
7572         /**
7573          * @event cellcontextmenu
7574          * Fires when a cell is right clicked
7575          * @param {Roo.bootstrap.Table} this
7576          * @param {Number} rowIndex
7577          * @param {Number} cellIndex
7578          * @param {Roo.EventObject} e
7579          */
7580          "cellcontextmenu" : true,
7581          /**
7582          * @event headercontextmenu
7583          * Fires when a header is right clicked
7584          * @param {Roo.bootstrap.Table} this
7585          * @param {Number} columnIndex
7586          * @param {Roo.EventObject} e
7587          */
7588         "headercontextmenu" : true
7589     });
7590 };
7591
7592 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7593     
7594     cls: false,
7595     align: false,
7596     bgcolor: false,
7597     border: false,
7598     cellpadding: false,
7599     cellspacing: false,
7600     frame: false,
7601     rules: false,
7602     sortable: false,
7603     summary: false,
7604     width: false,
7605     striped : false,
7606     scrollBody : false,
7607     bordered: false,
7608     hover:  false,
7609     condensed : false,
7610     responsive : false,
7611     sm : false,
7612     cm : false,
7613     store : false,
7614     loadMask : false,
7615     footerShow : true,
7616     headerShow : true,
7617   
7618     rowSelection : false,
7619     cellSelection : false,
7620     layout : false,
7621     
7622     // Roo.Element - the tbody
7623     mainBody: false,
7624     // Roo.Element - thead element
7625     mainHead: false,
7626     
7627     container: false, // used by gridpanel...
7628     
7629     lazyLoad : false,
7630     
7631     CSS : Roo.util.CSS,
7632     
7633     auto_hide_footer : false,
7634     
7635     getAutoCreate : function()
7636     {
7637         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7638         
7639         cfg = {
7640             tag: 'table',
7641             cls : 'table',
7642             cn : []
7643         };
7644         if (this.scrollBody) {
7645             cfg.cls += ' table-body-fixed';
7646         }    
7647         if (this.striped) {
7648             cfg.cls += ' table-striped';
7649         }
7650         
7651         if (this.hover) {
7652             cfg.cls += ' table-hover';
7653         }
7654         if (this.bordered) {
7655             cfg.cls += ' table-bordered';
7656         }
7657         if (this.condensed) {
7658             cfg.cls += ' table-condensed';
7659         }
7660         if (this.responsive) {
7661             cfg.cls += ' table-responsive';
7662         }
7663         
7664         if (this.cls) {
7665             cfg.cls+=  ' ' +this.cls;
7666         }
7667         
7668         // this lot should be simplifed...
7669         var _t = this;
7670         var cp = [
7671             'align',
7672             'bgcolor',
7673             'border',
7674             'cellpadding',
7675             'cellspacing',
7676             'frame',
7677             'rules',
7678             'sortable',
7679             'summary',
7680             'width'
7681         ].forEach(function(k) {
7682             if (_t[k]) {
7683                 cfg[k] = _t[k];
7684             }
7685         });
7686         
7687         
7688         if (this.layout) {
7689             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7690         }
7691         
7692         if(this.store || this.cm){
7693             if(this.headerShow){
7694                 cfg.cn.push(this.renderHeader());
7695             }
7696             
7697             cfg.cn.push(this.renderBody());
7698             
7699             if(this.footerShow){
7700                 cfg.cn.push(this.renderFooter());
7701             }
7702             // where does this come from?
7703             //cfg.cls+=  ' TableGrid';
7704         }
7705         
7706         return { cn : [ cfg ] };
7707     },
7708     
7709     initEvents : function()
7710     {   
7711         if(!this.store || !this.cm){
7712             return;
7713         }
7714         if (this.selModel) {
7715             this.selModel.initEvents();
7716         }
7717         
7718         
7719         //Roo.log('initEvents with ds!!!!');
7720         
7721         this.mainBody = this.el.select('tbody', true).first();
7722         this.mainHead = this.el.select('thead', true).first();
7723         this.mainFoot = this.el.select('tfoot', true).first();
7724         
7725         
7726         
7727         var _this = this;
7728         
7729         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7730             e.on('click', _this.sort, _this);
7731         });
7732         
7733         this.mainBody.on("click", this.onClick, this);
7734         this.mainBody.on("dblclick", this.onDblClick, this);
7735         
7736         // why is this done????? = it breaks dialogs??
7737         //this.parent().el.setStyle('position', 'relative');
7738         
7739         
7740         if (this.footer) {
7741             this.footer.parentId = this.id;
7742             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7743             
7744             if(this.lazyLoad){
7745                 this.el.select('tfoot tr td').first().addClass('hide');
7746             }
7747         } 
7748         
7749         if(this.loadMask) {
7750             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7751         }
7752         
7753         this.store.on('load', this.onLoad, this);
7754         this.store.on('beforeload', this.onBeforeLoad, this);
7755         this.store.on('update', this.onUpdate, this);
7756         this.store.on('add', this.onAdd, this);
7757         this.store.on("clear", this.clear, this);
7758         
7759         this.el.on("contextmenu", this.onContextMenu, this);
7760         
7761         this.mainBody.on('scroll', this.onBodyScroll, this);
7762         
7763         this.cm.on("headerchange", this.onHeaderChange, this);
7764         
7765         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7766         
7767     },
7768     
7769     onContextMenu : function(e, t)
7770     {
7771         this.processEvent("contextmenu", e);
7772     },
7773     
7774     processEvent : function(name, e)
7775     {
7776         if (name != 'touchstart' ) {
7777             this.fireEvent(name, e);    
7778         }
7779         
7780         var t = e.getTarget();
7781         
7782         var cell = Roo.get(t);
7783         
7784         if(!cell){
7785             return;
7786         }
7787         
7788         if(cell.findParent('tfoot', false, true)){
7789             return;
7790         }
7791         
7792         if(cell.findParent('thead', false, true)){
7793             
7794             if(e.getTarget().nodeName.toLowerCase() != 'th'){
7795                 cell = Roo.get(t).findParent('th', false, true);
7796                 if (!cell) {
7797                     Roo.log("failed to find th in thead?");
7798                     Roo.log(e.getTarget());
7799                     return;
7800                 }
7801             }
7802             
7803             var cellIndex = cell.dom.cellIndex;
7804             
7805             var ename = name == 'touchstart' ? 'click' : name;
7806             this.fireEvent("header" + ename, this, cellIndex, e);
7807             
7808             return;
7809         }
7810         
7811         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7812             cell = Roo.get(t).findParent('td', false, true);
7813             if (!cell) {
7814                 Roo.log("failed to find th in tbody?");
7815                 Roo.log(e.getTarget());
7816                 return;
7817             }
7818         }
7819         
7820         var row = cell.findParent('tr', false, true);
7821         var cellIndex = cell.dom.cellIndex;
7822         var rowIndex = row.dom.rowIndex - 1;
7823         
7824         if(row !== false){
7825             
7826             this.fireEvent("row" + name, this, rowIndex, e);
7827             
7828             if(cell !== false){
7829             
7830                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7831             }
7832         }
7833         
7834     },
7835     
7836     onMouseover : function(e, el)
7837     {
7838         var cell = Roo.get(el);
7839         
7840         if(!cell){
7841             return;
7842         }
7843         
7844         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7845             cell = cell.findParent('td', false, true);
7846         }
7847         
7848         var row = cell.findParent('tr', false, true);
7849         var cellIndex = cell.dom.cellIndex;
7850         var rowIndex = row.dom.rowIndex - 1; // start from 0
7851         
7852         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7853         
7854     },
7855     
7856     onMouseout : function(e, el)
7857     {
7858         var cell = Roo.get(el);
7859         
7860         if(!cell){
7861             return;
7862         }
7863         
7864         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7865             cell = cell.findParent('td', false, true);
7866         }
7867         
7868         var row = cell.findParent('tr', false, true);
7869         var cellIndex = cell.dom.cellIndex;
7870         var rowIndex = row.dom.rowIndex - 1; // start from 0
7871         
7872         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7873         
7874     },
7875     
7876     onClick : function(e, el)
7877     {
7878         var cell = Roo.get(el);
7879         
7880         if(!cell || (!this.cellSelection && !this.rowSelection)){
7881             return;
7882         }
7883         
7884         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7885             cell = cell.findParent('td', false, true);
7886         }
7887         
7888         if(!cell || typeof(cell) == 'undefined'){
7889             return;
7890         }
7891         
7892         var row = cell.findParent('tr', false, true);
7893         
7894         if(!row || typeof(row) == 'undefined'){
7895             return;
7896         }
7897         
7898         var cellIndex = cell.dom.cellIndex;
7899         var rowIndex = this.getRowIndex(row);
7900         
7901         // why??? - should these not be based on SelectionModel?
7902         if(this.cellSelection){
7903             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7904         }
7905         
7906         if(this.rowSelection){
7907             this.fireEvent('rowclick', this, row, rowIndex, e);
7908         }
7909         
7910         
7911     },
7912         
7913     onDblClick : function(e,el)
7914     {
7915         var cell = Roo.get(el);
7916         
7917         if(!cell || (!this.cellSelection && !this.rowSelection)){
7918             return;
7919         }
7920         
7921         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7922             cell = cell.findParent('td', false, true);
7923         }
7924         
7925         if(!cell || typeof(cell) == 'undefined'){
7926             return;
7927         }
7928         
7929         var row = cell.findParent('tr', false, true);
7930         
7931         if(!row || typeof(row) == 'undefined'){
7932             return;
7933         }
7934         
7935         var cellIndex = cell.dom.cellIndex;
7936         var rowIndex = this.getRowIndex(row);
7937         
7938         if(this.cellSelection){
7939             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7940         }
7941         
7942         if(this.rowSelection){
7943             this.fireEvent('rowdblclick', this, row, rowIndex, e);
7944         }
7945     },
7946     
7947     sort : function(e,el)
7948     {
7949         var col = Roo.get(el);
7950         
7951         if(!col.hasClass('sortable')){
7952             return;
7953         }
7954         
7955         var sort = col.attr('sort');
7956         var dir = 'ASC';
7957         
7958         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7959             dir = 'DESC';
7960         }
7961         
7962         this.store.sortInfo = {field : sort, direction : dir};
7963         
7964         if (this.footer) {
7965             Roo.log("calling footer first");
7966             this.footer.onClick('first');
7967         } else {
7968         
7969             this.store.load({ params : { start : 0 } });
7970         }
7971     },
7972     
7973     renderHeader : function()
7974     {
7975         var header = {
7976             tag: 'thead',
7977             cn : []
7978         };
7979         
7980         var cm = this.cm;
7981         this.totalWidth = 0;
7982         
7983         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7984             
7985             var config = cm.config[i];
7986             
7987             var c = {
7988                 tag: 'th',
7989                 cls : 'x-hcol-' + i,
7990                 style : '',
7991                 html: cm.getColumnHeader(i)
7992             };
7993             
7994             var hh = '';
7995             
7996             if(typeof(config.sortable) != 'undefined' && config.sortable){
7997                 c.cls = 'sortable';
7998                 c.html = '<i class="glyphicon"></i>' + c.html;
7999             }
8000             
8001             // could use BS4 hidden-..-down 
8002             
8003             if(typeof(config.lgHeader) != 'undefined'){
8004                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8005             }
8006             
8007             if(typeof(config.mdHeader) != 'undefined'){
8008                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8009             }
8010             
8011             if(typeof(config.smHeader) != 'undefined'){
8012                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8013             }
8014             
8015             if(typeof(config.xsHeader) != 'undefined'){
8016                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8017             }
8018             
8019             if(hh.length){
8020                 c.html = hh;
8021             }
8022             
8023             if(typeof(config.tooltip) != 'undefined'){
8024                 c.tooltip = config.tooltip;
8025             }
8026             
8027             if(typeof(config.colspan) != 'undefined'){
8028                 c.colspan = config.colspan;
8029             }
8030             
8031             if(typeof(config.hidden) != 'undefined' && config.hidden){
8032                 c.style += ' display:none;';
8033             }
8034             
8035             if(typeof(config.dataIndex) != 'undefined'){
8036                 c.sort = config.dataIndex;
8037             }
8038             
8039            
8040             
8041             if(typeof(config.align) != 'undefined' && config.align.length){
8042                 c.style += ' text-align:' + config.align + ';';
8043             }
8044             
8045             if(typeof(config.width) != 'undefined'){
8046                 c.style += ' width:' + config.width + 'px;';
8047                 this.totalWidth += config.width;
8048             } else {
8049                 this.totalWidth += 100; // assume minimum of 100 per column?
8050             }
8051             
8052             if(typeof(config.cls) != 'undefined'){
8053                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8054             }
8055             
8056             ['xs','sm','md','lg'].map(function(size){
8057                 
8058                 if(typeof(config[size]) == 'undefined'){
8059                     return;
8060                 }
8061                  
8062                 if (!config[size]) { // 0 = hidden
8063                     // BS 4 '0' is treated as hide that column and below.
8064                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8065                     return;
8066                 }
8067                 
8068                 c.cls += ' col-' + size + '-' + config[size] + (
8069                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8070                 );
8071                 
8072                 
8073             });
8074             
8075             header.cn.push(c)
8076         }
8077         
8078         return header;
8079     },
8080     
8081     renderBody : function()
8082     {
8083         var body = {
8084             tag: 'tbody',
8085             cn : [
8086                 {
8087                     tag: 'tr',
8088                     cn : [
8089                         {
8090                             tag : 'td',
8091                             colspan :  this.cm.getColumnCount()
8092                         }
8093                     ]
8094                 }
8095             ]
8096         };
8097         
8098         return body;
8099     },
8100     
8101     renderFooter : function()
8102     {
8103         var footer = {
8104             tag: 'tfoot',
8105             cn : [
8106                 {
8107                     tag: 'tr',
8108                     cn : [
8109                         {
8110                             tag : 'td',
8111                             colspan :  this.cm.getColumnCount()
8112                         }
8113                     ]
8114                 }
8115             ]
8116         };
8117         
8118         return footer;
8119     },
8120     
8121     
8122     
8123     onLoad : function()
8124     {
8125 //        Roo.log('ds onload');
8126         this.clear();
8127         
8128         var _this = this;
8129         var cm = this.cm;
8130         var ds = this.store;
8131         
8132         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8133             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8134             if (_this.store.sortInfo) {
8135                     
8136                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8137                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8138                 }
8139                 
8140                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8141                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8142                 }
8143             }
8144         });
8145         
8146         var tbody =  this.mainBody;
8147               
8148         if(ds.getCount() > 0){
8149             ds.data.each(function(d,rowIndex){
8150                 var row =  this.renderRow(cm, ds, rowIndex);
8151                 
8152                 tbody.createChild(row);
8153                 
8154                 var _this = this;
8155                 
8156                 if(row.cellObjects.length){
8157                     Roo.each(row.cellObjects, function(r){
8158                         _this.renderCellObject(r);
8159                     })
8160                 }
8161                 
8162             }, this);
8163         }
8164         
8165         var tfoot = this.el.select('tfoot', true).first();
8166         
8167         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8168             
8169             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8170             
8171             var total = this.ds.getTotalCount();
8172             
8173             if(this.footer.pageSize < total){
8174                 this.mainFoot.show();
8175             }
8176         }
8177         
8178         Roo.each(this.el.select('tbody td', true).elements, function(e){
8179             e.on('mouseover', _this.onMouseover, _this);
8180         });
8181         
8182         Roo.each(this.el.select('tbody td', true).elements, function(e){
8183             e.on('mouseout', _this.onMouseout, _this);
8184         });
8185         this.fireEvent('rowsrendered', this);
8186         
8187         this.autoSize();
8188     },
8189     
8190     
8191     onUpdate : function(ds,record)
8192     {
8193         this.refreshRow(record);
8194         this.autoSize();
8195     },
8196     
8197     onRemove : function(ds, record, index, isUpdate){
8198         if(isUpdate !== true){
8199             this.fireEvent("beforerowremoved", this, index, record);
8200         }
8201         var bt = this.mainBody.dom;
8202         
8203         var rows = this.el.select('tbody > tr', true).elements;
8204         
8205         if(typeof(rows[index]) != 'undefined'){
8206             bt.removeChild(rows[index].dom);
8207         }
8208         
8209 //        if(bt.rows[index]){
8210 //            bt.removeChild(bt.rows[index]);
8211 //        }
8212         
8213         if(isUpdate !== true){
8214             //this.stripeRows(index);
8215             //this.syncRowHeights(index, index);
8216             //this.layout();
8217             this.fireEvent("rowremoved", this, index, record);
8218         }
8219     },
8220     
8221     onAdd : function(ds, records, rowIndex)
8222     {
8223         //Roo.log('on Add called');
8224         // - note this does not handle multiple adding very well..
8225         var bt = this.mainBody.dom;
8226         for (var i =0 ; i < records.length;i++) {
8227             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8228             //Roo.log(records[i]);
8229             //Roo.log(this.store.getAt(rowIndex+i));
8230             this.insertRow(this.store, rowIndex + i, false);
8231             return;
8232         }
8233         
8234     },
8235     
8236     
8237     refreshRow : function(record){
8238         var ds = this.store, index;
8239         if(typeof record == 'number'){
8240             index = record;
8241             record = ds.getAt(index);
8242         }else{
8243             index = ds.indexOf(record);
8244         }
8245         this.insertRow(ds, index, true);
8246         this.autoSize();
8247         this.onRemove(ds, record, index+1, true);
8248         this.autoSize();
8249         //this.syncRowHeights(index, index);
8250         //this.layout();
8251         this.fireEvent("rowupdated", this, index, record);
8252     },
8253     
8254     insertRow : function(dm, rowIndex, isUpdate){
8255         
8256         if(!isUpdate){
8257             this.fireEvent("beforerowsinserted", this, rowIndex);
8258         }
8259             //var s = this.getScrollState();
8260         var row = this.renderRow(this.cm, this.store, rowIndex);
8261         // insert before rowIndex..
8262         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8263         
8264         var _this = this;
8265                 
8266         if(row.cellObjects.length){
8267             Roo.each(row.cellObjects, function(r){
8268                 _this.renderCellObject(r);
8269             })
8270         }
8271             
8272         if(!isUpdate){
8273             this.fireEvent("rowsinserted", this, rowIndex);
8274             //this.syncRowHeights(firstRow, lastRow);
8275             //this.stripeRows(firstRow);
8276             //this.layout();
8277         }
8278         
8279     },
8280     
8281     
8282     getRowDom : function(rowIndex)
8283     {
8284         var rows = this.el.select('tbody > tr', true).elements;
8285         
8286         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8287         
8288     },
8289     // returns the object tree for a tr..
8290   
8291     
8292     renderRow : function(cm, ds, rowIndex) 
8293     {
8294         var d = ds.getAt(rowIndex);
8295         
8296         var row = {
8297             tag : 'tr',
8298             cls : 'x-row-' + rowIndex,
8299             cn : []
8300         };
8301             
8302         var cellObjects = [];
8303         
8304         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8305             var config = cm.config[i];
8306             
8307             var renderer = cm.getRenderer(i);
8308             var value = '';
8309             var id = false;
8310             
8311             if(typeof(renderer) !== 'undefined'){
8312                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8313             }
8314             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8315             // and are rendered into the cells after the row is rendered - using the id for the element.
8316             
8317             if(typeof(value) === 'object'){
8318                 id = Roo.id();
8319                 cellObjects.push({
8320                     container : id,
8321                     cfg : value 
8322                 })
8323             }
8324             
8325             var rowcfg = {
8326                 record: d,
8327                 rowIndex : rowIndex,
8328                 colIndex : i,
8329                 rowClass : ''
8330             };
8331
8332             this.fireEvent('rowclass', this, rowcfg);
8333             
8334             var td = {
8335                 tag: 'td',
8336                 cls : rowcfg.rowClass + ' x-col-' + i,
8337                 style: '',
8338                 html: (typeof(value) === 'object') ? '' : value
8339             };
8340             
8341             if (id) {
8342                 td.id = id;
8343             }
8344             
8345             if(typeof(config.colspan) != 'undefined'){
8346                 td.colspan = config.colspan;
8347             }
8348             
8349             if(typeof(config.hidden) != 'undefined' && config.hidden){
8350                 td.style += ' display:none;';
8351             }
8352             
8353             if(typeof(config.align) != 'undefined' && config.align.length){
8354                 td.style += ' text-align:' + config.align + ';';
8355             }
8356             if(typeof(config.valign) != 'undefined' && config.valign.length){
8357                 td.style += ' vertical-align:' + config.valign + ';';
8358             }
8359             
8360             if(typeof(config.width) != 'undefined'){
8361                 td.style += ' width:' +  config.width + 'px;';
8362             }
8363             
8364             if(typeof(config.cursor) != 'undefined'){
8365                 td.style += ' cursor:' +  config.cursor + ';';
8366             }
8367             
8368             if(typeof(config.cls) != 'undefined'){
8369                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8370             }
8371             
8372             ['xs','sm','md','lg'].map(function(size){
8373                 
8374                 if(typeof(config[size]) == 'undefined'){
8375                     return;
8376                 }
8377                 
8378                 
8379                   
8380                 if (!config[size]) { // 0 = hidden
8381                     // BS 4 '0' is treated as hide that column and below.
8382                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8383                     return;
8384                 }
8385                 
8386                 td.cls += ' col-' + size + '-' + config[size] + (
8387                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8388                 );
8389                  
8390
8391             });
8392             
8393             row.cn.push(td);
8394            
8395         }
8396         
8397         row.cellObjects = cellObjects;
8398         
8399         return row;
8400           
8401     },
8402     
8403     
8404     
8405     onBeforeLoad : function()
8406     {
8407         
8408     },
8409      /**
8410      * Remove all rows
8411      */
8412     clear : function()
8413     {
8414         this.el.select('tbody', true).first().dom.innerHTML = '';
8415     },
8416     /**
8417      * Show or hide a row.
8418      * @param {Number} rowIndex to show or hide
8419      * @param {Boolean} state hide
8420      */
8421     setRowVisibility : function(rowIndex, state)
8422     {
8423         var bt = this.mainBody.dom;
8424         
8425         var rows = this.el.select('tbody > tr', true).elements;
8426         
8427         if(typeof(rows[rowIndex]) == 'undefined'){
8428             return;
8429         }
8430         rows[rowIndex].dom.style.display = state ? '' : 'none';
8431     },
8432     
8433     
8434     getSelectionModel : function(){
8435         if(!this.selModel){
8436             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8437         }
8438         return this.selModel;
8439     },
8440     /*
8441      * Render the Roo.bootstrap object from renderder
8442      */
8443     renderCellObject : function(r)
8444     {
8445         var _this = this;
8446         
8447         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8448         
8449         var t = r.cfg.render(r.container);
8450         
8451         if(r.cfg.cn){
8452             Roo.each(r.cfg.cn, function(c){
8453                 var child = {
8454                     container: t.getChildContainer(),
8455                     cfg: c
8456                 };
8457                 _this.renderCellObject(child);
8458             })
8459         }
8460     },
8461     
8462     getRowIndex : function(row)
8463     {
8464         var rowIndex = -1;
8465         
8466         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8467             if(el != row){
8468                 return;
8469             }
8470             
8471             rowIndex = index;
8472         });
8473         
8474         return rowIndex;
8475     },
8476      /**
8477      * Returns the grid's underlying element = used by panel.Grid
8478      * @return {Element} The element
8479      */
8480     getGridEl : function(){
8481         return this.el;
8482     },
8483      /**
8484      * Forces a resize - used by panel.Grid
8485      * @return {Element} The element
8486      */
8487     autoSize : function()
8488     {
8489         //var ctr = Roo.get(this.container.dom.parentElement);
8490         var ctr = Roo.get(this.el.dom);
8491         
8492         var thd = this.getGridEl().select('thead',true).first();
8493         var tbd = this.getGridEl().select('tbody', true).first();
8494         var tfd = this.getGridEl().select('tfoot', true).first();
8495         
8496         var cw = ctr.getWidth();
8497         
8498         if (tbd) {
8499             
8500             tbd.setWidth(ctr.getWidth());
8501             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8502             // this needs fixing for various usage - currently only hydra job advers I think..
8503             //tdb.setHeight(
8504             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8505             //); 
8506             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8507             cw -= barsize;
8508         }
8509         cw = Math.max(cw, this.totalWidth);
8510         this.getGridEl().select('tr',true).setWidth(cw);
8511         // resize 'expandable coloumn?
8512         
8513         return; // we doe not have a view in this design..
8514         
8515     },
8516     onBodyScroll: function()
8517     {
8518         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8519         if(this.mainHead){
8520             this.mainHead.setStyle({
8521                 'position' : 'relative',
8522                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8523             });
8524         }
8525         
8526         if(this.lazyLoad){
8527             
8528             var scrollHeight = this.mainBody.dom.scrollHeight;
8529             
8530             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8531             
8532             var height = this.mainBody.getHeight();
8533             
8534             if(scrollHeight - height == scrollTop) {
8535                 
8536                 var total = this.ds.getTotalCount();
8537                 
8538                 if(this.footer.cursor + this.footer.pageSize < total){
8539                     
8540                     this.footer.ds.load({
8541                         params : {
8542                             start : this.footer.cursor + this.footer.pageSize,
8543                             limit : this.footer.pageSize
8544                         },
8545                         add : true
8546                     });
8547                 }
8548             }
8549             
8550         }
8551     },
8552     
8553     onHeaderChange : function()
8554     {
8555         var header = this.renderHeader();
8556         var table = this.el.select('table', true).first();
8557         
8558         this.mainHead.remove();
8559         this.mainHead = table.createChild(header, this.mainBody, false);
8560     },
8561     
8562     onHiddenChange : function(colModel, colIndex, hidden)
8563     {
8564         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8565         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8566         
8567         this.CSS.updateRule(thSelector, "display", "");
8568         this.CSS.updateRule(tdSelector, "display", "");
8569         
8570         if(hidden){
8571             this.CSS.updateRule(thSelector, "display", "none");
8572             this.CSS.updateRule(tdSelector, "display", "none");
8573         }
8574         
8575         this.onHeaderChange();
8576         this.onLoad();
8577     },
8578     
8579     setColumnWidth: function(col_index, width)
8580     {
8581         // width = "md-2 xs-2..."
8582         if(!this.colModel.config[col_index]) {
8583             return;
8584         }
8585         
8586         var w = width.split(" ");
8587         
8588         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8589         
8590         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8591         
8592         
8593         for(var j = 0; j < w.length; j++) {
8594             
8595             if(!w[j]) {
8596                 continue;
8597             }
8598             
8599             var size_cls = w[j].split("-");
8600             
8601             if(!Number.isInteger(size_cls[1] * 1)) {
8602                 continue;
8603             }
8604             
8605             if(!this.colModel.config[col_index][size_cls[0]]) {
8606                 continue;
8607             }
8608             
8609             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8610                 continue;
8611             }
8612             
8613             h_row[0].classList.replace(
8614                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8615                 "col-"+size_cls[0]+"-"+size_cls[1]
8616             );
8617             
8618             for(var i = 0; i < rows.length; i++) {
8619                 
8620                 var size_cls = w[j].split("-");
8621                 
8622                 if(!Number.isInteger(size_cls[1] * 1)) {
8623                     continue;
8624                 }
8625                 
8626                 if(!this.colModel.config[col_index][size_cls[0]]) {
8627                     continue;
8628                 }
8629                 
8630                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8631                     continue;
8632                 }
8633                 
8634                 rows[i].classList.replace(
8635                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8636                     "col-"+size_cls[0]+"-"+size_cls[1]
8637                 );
8638             }
8639             
8640             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8641         }
8642     }
8643 });
8644
8645  
8646
8647  /*
8648  * - LGPL
8649  *
8650  * table cell
8651  * 
8652  */
8653
8654 /**
8655  * @class Roo.bootstrap.TableCell
8656  * @extends Roo.bootstrap.Component
8657  * Bootstrap TableCell class
8658  * @cfg {String} html cell contain text
8659  * @cfg {String} cls cell class
8660  * @cfg {String} tag cell tag (td|th) default td
8661  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8662  * @cfg {String} align Aligns the content in a cell
8663  * @cfg {String} axis Categorizes cells
8664  * @cfg {String} bgcolor Specifies the background color of a cell
8665  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8666  * @cfg {Number} colspan Specifies the number of columns a cell should span
8667  * @cfg {String} headers Specifies one or more header cells a cell is related to
8668  * @cfg {Number} height Sets the height of a cell
8669  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8670  * @cfg {Number} rowspan Sets the number of rows a cell should span
8671  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8672  * @cfg {String} valign Vertical aligns the content in a cell
8673  * @cfg {Number} width Specifies the width of a cell
8674  * 
8675  * @constructor
8676  * Create a new TableCell
8677  * @param {Object} config The config object
8678  */
8679
8680 Roo.bootstrap.TableCell = function(config){
8681     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8682 };
8683
8684 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8685     
8686     html: false,
8687     cls: false,
8688     tag: false,
8689     abbr: false,
8690     align: false,
8691     axis: false,
8692     bgcolor: false,
8693     charoff: false,
8694     colspan: false,
8695     headers: false,
8696     height: false,
8697     nowrap: false,
8698     rowspan: false,
8699     scope: false,
8700     valign: false,
8701     width: false,
8702     
8703     
8704     getAutoCreate : function(){
8705         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8706         
8707         cfg = {
8708             tag: 'td'
8709         };
8710         
8711         if(this.tag){
8712             cfg.tag = this.tag;
8713         }
8714         
8715         if (this.html) {
8716             cfg.html=this.html
8717         }
8718         if (this.cls) {
8719             cfg.cls=this.cls
8720         }
8721         if (this.abbr) {
8722             cfg.abbr=this.abbr
8723         }
8724         if (this.align) {
8725             cfg.align=this.align
8726         }
8727         if (this.axis) {
8728             cfg.axis=this.axis
8729         }
8730         if (this.bgcolor) {
8731             cfg.bgcolor=this.bgcolor
8732         }
8733         if (this.charoff) {
8734             cfg.charoff=this.charoff
8735         }
8736         if (this.colspan) {
8737             cfg.colspan=this.colspan
8738         }
8739         if (this.headers) {
8740             cfg.headers=this.headers
8741         }
8742         if (this.height) {
8743             cfg.height=this.height
8744         }
8745         if (this.nowrap) {
8746             cfg.nowrap=this.nowrap
8747         }
8748         if (this.rowspan) {
8749             cfg.rowspan=this.rowspan
8750         }
8751         if (this.scope) {
8752             cfg.scope=this.scope
8753         }
8754         if (this.valign) {
8755             cfg.valign=this.valign
8756         }
8757         if (this.width) {
8758             cfg.width=this.width
8759         }
8760         
8761         
8762         return cfg;
8763     }
8764    
8765 });
8766
8767  
8768
8769  /*
8770  * - LGPL
8771  *
8772  * table row
8773  * 
8774  */
8775
8776 /**
8777  * @class Roo.bootstrap.TableRow
8778  * @extends Roo.bootstrap.Component
8779  * Bootstrap TableRow class
8780  * @cfg {String} cls row class
8781  * @cfg {String} align Aligns the content in a table row
8782  * @cfg {String} bgcolor Specifies a background color for a table row
8783  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8784  * @cfg {String} valign Vertical aligns the content in a table row
8785  * 
8786  * @constructor
8787  * Create a new TableRow
8788  * @param {Object} config The config object
8789  */
8790
8791 Roo.bootstrap.TableRow = function(config){
8792     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8793 };
8794
8795 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
8796     
8797     cls: false,
8798     align: false,
8799     bgcolor: false,
8800     charoff: false,
8801     valign: false,
8802     
8803     getAutoCreate : function(){
8804         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8805         
8806         cfg = {
8807             tag: 'tr'
8808         };
8809             
8810         if(this.cls){
8811             cfg.cls = this.cls;
8812         }
8813         if(this.align){
8814             cfg.align = this.align;
8815         }
8816         if(this.bgcolor){
8817             cfg.bgcolor = this.bgcolor;
8818         }
8819         if(this.charoff){
8820             cfg.charoff = this.charoff;
8821         }
8822         if(this.valign){
8823             cfg.valign = this.valign;
8824         }
8825         
8826         return cfg;
8827     }
8828    
8829 });
8830
8831  
8832
8833  /*
8834  * - LGPL
8835  *
8836  * table body
8837  * 
8838  */
8839
8840 /**
8841  * @class Roo.bootstrap.TableBody
8842  * @extends Roo.bootstrap.Component
8843  * Bootstrap TableBody class
8844  * @cfg {String} cls element class
8845  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8846  * @cfg {String} align Aligns the content inside the element
8847  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8848  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8849  * 
8850  * @constructor
8851  * Create a new TableBody
8852  * @param {Object} config The config object
8853  */
8854
8855 Roo.bootstrap.TableBody = function(config){
8856     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8857 };
8858
8859 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
8860     
8861     cls: false,
8862     tag: false,
8863     align: false,
8864     charoff: false,
8865     valign: false,
8866     
8867     getAutoCreate : function(){
8868         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8869         
8870         cfg = {
8871             tag: 'tbody'
8872         };
8873             
8874         if (this.cls) {
8875             cfg.cls=this.cls
8876         }
8877         if(this.tag){
8878             cfg.tag = this.tag;
8879         }
8880         
8881         if(this.align){
8882             cfg.align = this.align;
8883         }
8884         if(this.charoff){
8885             cfg.charoff = this.charoff;
8886         }
8887         if(this.valign){
8888             cfg.valign = this.valign;
8889         }
8890         
8891         return cfg;
8892     }
8893     
8894     
8895 //    initEvents : function()
8896 //    {
8897 //        
8898 //        if(!this.store){
8899 //            return;
8900 //        }
8901 //        
8902 //        this.store = Roo.factory(this.store, Roo.data);
8903 //        this.store.on('load', this.onLoad, this);
8904 //        
8905 //        this.store.load();
8906 //        
8907 //    },
8908 //    
8909 //    onLoad: function () 
8910 //    {   
8911 //        this.fireEvent('load', this);
8912 //    }
8913 //    
8914 //   
8915 });
8916
8917  
8918
8919  /*
8920  * Based on:
8921  * Ext JS Library 1.1.1
8922  * Copyright(c) 2006-2007, Ext JS, LLC.
8923  *
8924  * Originally Released Under LGPL - original licence link has changed is not relivant.
8925  *
8926  * Fork - LGPL
8927  * <script type="text/javascript">
8928  */
8929
8930 // as we use this in bootstrap.
8931 Roo.namespace('Roo.form');
8932  /**
8933  * @class Roo.form.Action
8934  * Internal Class used to handle form actions
8935  * @constructor
8936  * @param {Roo.form.BasicForm} el The form element or its id
8937  * @param {Object} config Configuration options
8938  */
8939
8940  
8941  
8942 // define the action interface
8943 Roo.form.Action = function(form, options){
8944     this.form = form;
8945     this.options = options || {};
8946 };
8947 /**
8948  * Client Validation Failed
8949  * @const 
8950  */
8951 Roo.form.Action.CLIENT_INVALID = 'client';
8952 /**
8953  * Server Validation Failed
8954  * @const 
8955  */
8956 Roo.form.Action.SERVER_INVALID = 'server';
8957  /**
8958  * Connect to Server Failed
8959  * @const 
8960  */
8961 Roo.form.Action.CONNECT_FAILURE = 'connect';
8962 /**
8963  * Reading Data from Server Failed
8964  * @const 
8965  */
8966 Roo.form.Action.LOAD_FAILURE = 'load';
8967
8968 Roo.form.Action.prototype = {
8969     type : 'default',
8970     failureType : undefined,
8971     response : undefined,
8972     result : undefined,
8973
8974     // interface method
8975     run : function(options){
8976
8977     },
8978
8979     // interface method
8980     success : function(response){
8981
8982     },
8983
8984     // interface method
8985     handleResponse : function(response){
8986
8987     },
8988
8989     // default connection failure
8990     failure : function(response){
8991         
8992         this.response = response;
8993         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8994         this.form.afterAction(this, false);
8995     },
8996
8997     processResponse : function(response){
8998         this.response = response;
8999         if(!response.responseText){
9000             return true;
9001         }
9002         this.result = this.handleResponse(response);
9003         return this.result;
9004     },
9005
9006     // utility functions used internally
9007     getUrl : function(appendParams){
9008         var url = this.options.url || this.form.url || this.form.el.dom.action;
9009         if(appendParams){
9010             var p = this.getParams();
9011             if(p){
9012                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9013             }
9014         }
9015         return url;
9016     },
9017
9018     getMethod : function(){
9019         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9020     },
9021
9022     getParams : function(){
9023         var bp = this.form.baseParams;
9024         var p = this.options.params;
9025         if(p){
9026             if(typeof p == "object"){
9027                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9028             }else if(typeof p == 'string' && bp){
9029                 p += '&' + Roo.urlEncode(bp);
9030             }
9031         }else if(bp){
9032             p = Roo.urlEncode(bp);
9033         }
9034         return p;
9035     },
9036
9037     createCallback : function(){
9038         return {
9039             success: this.success,
9040             failure: this.failure,
9041             scope: this,
9042             timeout: (this.form.timeout*1000),
9043             upload: this.form.fileUpload ? this.success : undefined
9044         };
9045     }
9046 };
9047
9048 Roo.form.Action.Submit = function(form, options){
9049     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9050 };
9051
9052 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9053     type : 'submit',
9054
9055     haveProgress : false,
9056     uploadComplete : false,
9057     
9058     // uploadProgress indicator.
9059     uploadProgress : function()
9060     {
9061         if (!this.form.progressUrl) {
9062             return;
9063         }
9064         
9065         if (!this.haveProgress) {
9066             Roo.MessageBox.progress("Uploading", "Uploading");
9067         }
9068         if (this.uploadComplete) {
9069            Roo.MessageBox.hide();
9070            return;
9071         }
9072         
9073         this.haveProgress = true;
9074    
9075         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9076         
9077         var c = new Roo.data.Connection();
9078         c.request({
9079             url : this.form.progressUrl,
9080             params: {
9081                 id : uid
9082             },
9083             method: 'GET',
9084             success : function(req){
9085                //console.log(data);
9086                 var rdata = false;
9087                 var edata;
9088                 try  {
9089                    rdata = Roo.decode(req.responseText)
9090                 } catch (e) {
9091                     Roo.log("Invalid data from server..");
9092                     Roo.log(edata);
9093                     return;
9094                 }
9095                 if (!rdata || !rdata.success) {
9096                     Roo.log(rdata);
9097                     Roo.MessageBox.alert(Roo.encode(rdata));
9098                     return;
9099                 }
9100                 var data = rdata.data;
9101                 
9102                 if (this.uploadComplete) {
9103                    Roo.MessageBox.hide();
9104                    return;
9105                 }
9106                    
9107                 if (data){
9108                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9109                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9110                     );
9111                 }
9112                 this.uploadProgress.defer(2000,this);
9113             },
9114        
9115             failure: function(data) {
9116                 Roo.log('progress url failed ');
9117                 Roo.log(data);
9118             },
9119             scope : this
9120         });
9121            
9122     },
9123     
9124     
9125     run : function()
9126     {
9127         // run get Values on the form, so it syncs any secondary forms.
9128         this.form.getValues();
9129         
9130         var o = this.options;
9131         var method = this.getMethod();
9132         var isPost = method == 'POST';
9133         if(o.clientValidation === false || this.form.isValid()){
9134             
9135             if (this.form.progressUrl) {
9136                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9137                     (new Date() * 1) + '' + Math.random());
9138                     
9139             } 
9140             
9141             
9142             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9143                 form:this.form.el.dom,
9144                 url:this.getUrl(!isPost),
9145                 method: method,
9146                 params:isPost ? this.getParams() : null,
9147                 isUpload: this.form.fileUpload,
9148                 formData : this.form.formData
9149             }));
9150             
9151             this.uploadProgress();
9152
9153         }else if (o.clientValidation !== false){ // client validation failed
9154             this.failureType = Roo.form.Action.CLIENT_INVALID;
9155             this.form.afterAction(this, false);
9156         }
9157     },
9158
9159     success : function(response)
9160     {
9161         this.uploadComplete= true;
9162         if (this.haveProgress) {
9163             Roo.MessageBox.hide();
9164         }
9165         
9166         
9167         var result = this.processResponse(response);
9168         if(result === true || result.success){
9169             this.form.afterAction(this, true);
9170             return;
9171         }
9172         if(result.errors){
9173             this.form.markInvalid(result.errors);
9174             this.failureType = Roo.form.Action.SERVER_INVALID;
9175         }
9176         this.form.afterAction(this, false);
9177     },
9178     failure : function(response)
9179     {
9180         this.uploadComplete= true;
9181         if (this.haveProgress) {
9182             Roo.MessageBox.hide();
9183         }
9184         
9185         this.response = response;
9186         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9187         this.form.afterAction(this, false);
9188     },
9189     
9190     handleResponse : function(response){
9191         if(this.form.errorReader){
9192             var rs = this.form.errorReader.read(response);
9193             var errors = [];
9194             if(rs.records){
9195                 for(var i = 0, len = rs.records.length; i < len; i++) {
9196                     var r = rs.records[i];
9197                     errors[i] = r.data;
9198                 }
9199             }
9200             if(errors.length < 1){
9201                 errors = null;
9202             }
9203             return {
9204                 success : rs.success,
9205                 errors : errors
9206             };
9207         }
9208         var ret = false;
9209         try {
9210             ret = Roo.decode(response.responseText);
9211         } catch (e) {
9212             ret = {
9213                 success: false,
9214                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9215                 errors : []
9216             };
9217         }
9218         return ret;
9219         
9220     }
9221 });
9222
9223
9224 Roo.form.Action.Load = function(form, options){
9225     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9226     this.reader = this.form.reader;
9227 };
9228
9229 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9230     type : 'load',
9231
9232     run : function(){
9233         
9234         Roo.Ajax.request(Roo.apply(
9235                 this.createCallback(), {
9236                     method:this.getMethod(),
9237                     url:this.getUrl(false),
9238                     params:this.getParams()
9239         }));
9240     },
9241
9242     success : function(response){
9243         
9244         var result = this.processResponse(response);
9245         if(result === true || !result.success || !result.data){
9246             this.failureType = Roo.form.Action.LOAD_FAILURE;
9247             this.form.afterAction(this, false);
9248             return;
9249         }
9250         this.form.clearInvalid();
9251         this.form.setValues(result.data);
9252         this.form.afterAction(this, true);
9253     },
9254
9255     handleResponse : function(response){
9256         if(this.form.reader){
9257             var rs = this.form.reader.read(response);
9258             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9259             return {
9260                 success : rs.success,
9261                 data : data
9262             };
9263         }
9264         return Roo.decode(response.responseText);
9265     }
9266 });
9267
9268 Roo.form.Action.ACTION_TYPES = {
9269     'load' : Roo.form.Action.Load,
9270     'submit' : Roo.form.Action.Submit
9271 };/*
9272  * - LGPL
9273  *
9274  * form
9275  *
9276  */
9277
9278 /**
9279  * @class Roo.bootstrap.Form
9280  * @extends Roo.bootstrap.Component
9281  * Bootstrap Form class
9282  * @cfg {String} method  GET | POST (default POST)
9283  * @cfg {String} labelAlign top | left (default top)
9284  * @cfg {String} align left  | right - for navbars
9285  * @cfg {Boolean} loadMask load mask when submit (default true)
9286
9287  *
9288  * @constructor
9289  * Create a new Form
9290  * @param {Object} config The config object
9291  */
9292
9293
9294 Roo.bootstrap.Form = function(config){
9295     
9296     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9297     
9298     Roo.bootstrap.Form.popover.apply();
9299     
9300     this.addEvents({
9301         /**
9302          * @event clientvalidation
9303          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9304          * @param {Form} this
9305          * @param {Boolean} valid true if the form has passed client-side validation
9306          */
9307         clientvalidation: true,
9308         /**
9309          * @event beforeaction
9310          * Fires before any action is performed. Return false to cancel the action.
9311          * @param {Form} this
9312          * @param {Action} action The action to be performed
9313          */
9314         beforeaction: true,
9315         /**
9316          * @event actionfailed
9317          * Fires when an action fails.
9318          * @param {Form} this
9319          * @param {Action} action The action that failed
9320          */
9321         actionfailed : true,
9322         /**
9323          * @event actioncomplete
9324          * Fires when an action is completed.
9325          * @param {Form} this
9326          * @param {Action} action The action that completed
9327          */
9328         actioncomplete : true
9329     });
9330 };
9331
9332 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9333
9334      /**
9335      * @cfg {String} method
9336      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9337      */
9338     method : 'POST',
9339     /**
9340      * @cfg {String} url
9341      * The URL to use for form actions if one isn't supplied in the action options.
9342      */
9343     /**
9344      * @cfg {Boolean} fileUpload
9345      * Set to true if this form is a file upload.
9346      */
9347
9348     /**
9349      * @cfg {Object} baseParams
9350      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9351      */
9352
9353     /**
9354      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9355      */
9356     timeout: 30,
9357     /**
9358      * @cfg {Sting} align (left|right) for navbar forms
9359      */
9360     align : 'left',
9361
9362     // private
9363     activeAction : null,
9364
9365     /**
9366      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9367      * element by passing it or its id or mask the form itself by passing in true.
9368      * @type Mixed
9369      */
9370     waitMsgTarget : false,
9371
9372     loadMask : true,
9373     
9374     /**
9375      * @cfg {Boolean} errorMask (true|false) default false
9376      */
9377     errorMask : false,
9378     
9379     /**
9380      * @cfg {Number} maskOffset Default 100
9381      */
9382     maskOffset : 100,
9383     
9384     /**
9385      * @cfg {Boolean} maskBody
9386      */
9387     maskBody : false,
9388
9389     getAutoCreate : function(){
9390
9391         var cfg = {
9392             tag: 'form',
9393             method : this.method || 'POST',
9394             id : this.id || Roo.id(),
9395             cls : ''
9396         };
9397         if (this.parent().xtype.match(/^Nav/)) {
9398             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9399
9400         }
9401
9402         if (this.labelAlign == 'left' ) {
9403             cfg.cls += ' form-horizontal';
9404         }
9405
9406
9407         return cfg;
9408     },
9409     initEvents : function()
9410     {
9411         this.el.on('submit', this.onSubmit, this);
9412         // this was added as random key presses on the form where triggering form submit.
9413         this.el.on('keypress', function(e) {
9414             if (e.getCharCode() != 13) {
9415                 return true;
9416             }
9417             // we might need to allow it for textareas.. and some other items.
9418             // check e.getTarget().
9419
9420             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9421                 return true;
9422             }
9423
9424             Roo.log("keypress blocked");
9425
9426             e.preventDefault();
9427             return false;
9428         });
9429         
9430     },
9431     // private
9432     onSubmit : function(e){
9433         e.stopEvent();
9434     },
9435
9436      /**
9437      * Returns true if client-side validation on the form is successful.
9438      * @return Boolean
9439      */
9440     isValid : function(){
9441         var items = this.getItems();
9442         var valid = true;
9443         var target = false;
9444         
9445         items.each(function(f){
9446             
9447             if(f.validate()){
9448                 return;
9449             }
9450             
9451             Roo.log('invalid field: ' + f.name);
9452             
9453             valid = false;
9454
9455             if(!target && f.el.isVisible(true)){
9456                 target = f;
9457             }
9458            
9459         });
9460         
9461         if(this.errorMask && !valid){
9462             Roo.bootstrap.Form.popover.mask(this, target);
9463         }
9464         
9465         return valid;
9466     },
9467     
9468     /**
9469      * Returns true if any fields in this form have changed since their original load.
9470      * @return Boolean
9471      */
9472     isDirty : function(){
9473         var dirty = false;
9474         var items = this.getItems();
9475         items.each(function(f){
9476            if(f.isDirty()){
9477                dirty = true;
9478                return false;
9479            }
9480            return true;
9481         });
9482         return dirty;
9483     },
9484      /**
9485      * Performs a predefined action (submit or load) or custom actions you define on this form.
9486      * @param {String} actionName The name of the action type
9487      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9488      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9489      * accept other config options):
9490      * <pre>
9491 Property          Type             Description
9492 ----------------  ---------------  ----------------------------------------------------------------------------------
9493 url               String           The url for the action (defaults to the form's url)
9494 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9495 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9496 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9497                                    validate the form on the client (defaults to false)
9498      * </pre>
9499      * @return {BasicForm} this
9500      */
9501     doAction : function(action, options){
9502         if(typeof action == 'string'){
9503             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9504         }
9505         if(this.fireEvent('beforeaction', this, action) !== false){
9506             this.beforeAction(action);
9507             action.run.defer(100, action);
9508         }
9509         return this;
9510     },
9511
9512     // private
9513     beforeAction : function(action){
9514         var o = action.options;
9515         
9516         if(this.loadMask){
9517             
9518             if(this.maskBody){
9519                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9520             } else {
9521                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9522             }
9523         }
9524         // not really supported yet.. ??
9525
9526         //if(this.waitMsgTarget === true){
9527         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9528         //}else if(this.waitMsgTarget){
9529         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9530         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9531         //}else {
9532         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9533        // }
9534
9535     },
9536
9537     // private
9538     afterAction : function(action, success){
9539         this.activeAction = null;
9540         var o = action.options;
9541
9542         if(this.loadMask){
9543             
9544             if(this.maskBody){
9545                 Roo.get(document.body).unmask();
9546             } else {
9547                 this.el.unmask();
9548             }
9549         }
9550         
9551         //if(this.waitMsgTarget === true){
9552 //            this.el.unmask();
9553         //}else if(this.waitMsgTarget){
9554         //    this.waitMsgTarget.unmask();
9555         //}else{
9556         //    Roo.MessageBox.updateProgress(1);
9557         //    Roo.MessageBox.hide();
9558        // }
9559         //
9560         if(success){
9561             if(o.reset){
9562                 this.reset();
9563             }
9564             Roo.callback(o.success, o.scope, [this, action]);
9565             this.fireEvent('actioncomplete', this, action);
9566
9567         }else{
9568
9569             // failure condition..
9570             // we have a scenario where updates need confirming.
9571             // eg. if a locking scenario exists..
9572             // we look for { errors : { needs_confirm : true }} in the response.
9573             if (
9574                 (typeof(action.result) != 'undefined')  &&
9575                 (typeof(action.result.errors) != 'undefined')  &&
9576                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9577            ){
9578                 var _t = this;
9579                 Roo.log("not supported yet");
9580                  /*
9581
9582                 Roo.MessageBox.confirm(
9583                     "Change requires confirmation",
9584                     action.result.errorMsg,
9585                     function(r) {
9586                         if (r != 'yes') {
9587                             return;
9588                         }
9589                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9590                     }
9591
9592                 );
9593                 */
9594
9595
9596                 return;
9597             }
9598
9599             Roo.callback(o.failure, o.scope, [this, action]);
9600             // show an error message if no failed handler is set..
9601             if (!this.hasListener('actionfailed')) {
9602                 Roo.log("need to add dialog support");
9603                 /*
9604                 Roo.MessageBox.alert("Error",
9605                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9606                         action.result.errorMsg :
9607                         "Saving Failed, please check your entries or try again"
9608                 );
9609                 */
9610             }
9611
9612             this.fireEvent('actionfailed', this, action);
9613         }
9614
9615     },
9616     /**
9617      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9618      * @param {String} id The value to search for
9619      * @return Field
9620      */
9621     findField : function(id){
9622         var items = this.getItems();
9623         var field = items.get(id);
9624         if(!field){
9625              items.each(function(f){
9626                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9627                     field = f;
9628                     return false;
9629                 }
9630                 return true;
9631             });
9632         }
9633         return field || null;
9634     },
9635      /**
9636      * Mark fields in this form invalid in bulk.
9637      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9638      * @return {BasicForm} this
9639      */
9640     markInvalid : function(errors){
9641         if(errors instanceof Array){
9642             for(var i = 0, len = errors.length; i < len; i++){
9643                 var fieldError = errors[i];
9644                 var f = this.findField(fieldError.id);
9645                 if(f){
9646                     f.markInvalid(fieldError.msg);
9647                 }
9648             }
9649         }else{
9650             var field, id;
9651             for(id in errors){
9652                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9653                     field.markInvalid(errors[id]);
9654                 }
9655             }
9656         }
9657         //Roo.each(this.childForms || [], function (f) {
9658         //    f.markInvalid(errors);
9659         //});
9660
9661         return this;
9662     },
9663
9664     /**
9665      * Set values for fields in this form in bulk.
9666      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9667      * @return {BasicForm} this
9668      */
9669     setValues : function(values){
9670         if(values instanceof Array){ // array of objects
9671             for(var i = 0, len = values.length; i < len; i++){
9672                 var v = values[i];
9673                 var f = this.findField(v.id);
9674                 if(f){
9675                     f.setValue(v.value);
9676                     if(this.trackResetOnLoad){
9677                         f.originalValue = f.getValue();
9678                     }
9679                 }
9680             }
9681         }else{ // object hash
9682             var field, id;
9683             for(id in values){
9684                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9685
9686                     if (field.setFromData &&
9687                         field.valueField &&
9688                         field.displayField &&
9689                         // combos' with local stores can
9690                         // be queried via setValue()
9691                         // to set their value..
9692                         (field.store && !field.store.isLocal)
9693                         ) {
9694                         // it's a combo
9695                         var sd = { };
9696                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9697                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9698                         field.setFromData(sd);
9699
9700                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9701                         
9702                         field.setFromData(values);
9703                         
9704                     } else {
9705                         field.setValue(values[id]);
9706                     }
9707
9708
9709                     if(this.trackResetOnLoad){
9710                         field.originalValue = field.getValue();
9711                     }
9712                 }
9713             }
9714         }
9715
9716         //Roo.each(this.childForms || [], function (f) {
9717         //    f.setValues(values);
9718         //});
9719
9720         return this;
9721     },
9722
9723     /**
9724      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9725      * they are returned as an array.
9726      * @param {Boolean} asString
9727      * @return {Object}
9728      */
9729     getValues : function(asString){
9730         //if (this.childForms) {
9731             // copy values from the child forms
9732         //    Roo.each(this.childForms, function (f) {
9733         //        this.setValues(f.getValues());
9734         //    }, this);
9735         //}
9736
9737
9738
9739         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9740         if(asString === true){
9741             return fs;
9742         }
9743         return Roo.urlDecode(fs);
9744     },
9745
9746     /**
9747      * Returns the fields in this form as an object with key/value pairs.
9748      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9749      * @return {Object}
9750      */
9751     getFieldValues : function(with_hidden)
9752     {
9753         var items = this.getItems();
9754         var ret = {};
9755         items.each(function(f){
9756             
9757             if (!f.getName()) {
9758                 return;
9759             }
9760             
9761             var v = f.getValue();
9762             
9763             if (f.inputType =='radio') {
9764                 if (typeof(ret[f.getName()]) == 'undefined') {
9765                     ret[f.getName()] = ''; // empty..
9766                 }
9767
9768                 if (!f.el.dom.checked) {
9769                     return;
9770
9771                 }
9772                 v = f.el.dom.value;
9773
9774             }
9775             
9776             if(f.xtype == 'MoneyField'){
9777                 ret[f.currencyName] = f.getCurrency();
9778             }
9779
9780             // not sure if this supported any more..
9781             if ((typeof(v) == 'object') && f.getRawValue) {
9782                 v = f.getRawValue() ; // dates..
9783             }
9784             // combo boxes where name != hiddenName...
9785             if (f.name !== false && f.name != '' && f.name != f.getName()) {
9786                 ret[f.name] = f.getRawValue();
9787             }
9788             ret[f.getName()] = v;
9789         });
9790
9791         return ret;
9792     },
9793
9794     /**
9795      * Clears all invalid messages in this form.
9796      * @return {BasicForm} this
9797      */
9798     clearInvalid : function(){
9799         var items = this.getItems();
9800
9801         items.each(function(f){
9802            f.clearInvalid();
9803         });
9804
9805         return this;
9806     },
9807
9808     /**
9809      * Resets this form.
9810      * @return {BasicForm} this
9811      */
9812     reset : function(){
9813         var items = this.getItems();
9814         items.each(function(f){
9815             f.reset();
9816         });
9817
9818         Roo.each(this.childForms || [], function (f) {
9819             f.reset();
9820         });
9821
9822
9823         return this;
9824     },
9825     
9826     getItems : function()
9827     {
9828         var r=new Roo.util.MixedCollection(false, function(o){
9829             return o.id || (o.id = Roo.id());
9830         });
9831         var iter = function(el) {
9832             if (el.inputEl) {
9833                 r.add(el);
9834             }
9835             if (!el.items) {
9836                 return;
9837             }
9838             Roo.each(el.items,function(e) {
9839                 iter(e);
9840             });
9841         };
9842
9843         iter(this);
9844         return r;
9845     },
9846     
9847     hideFields : function(items)
9848     {
9849         Roo.each(items, function(i){
9850             
9851             var f = this.findField(i);
9852             
9853             if(!f){
9854                 return;
9855             }
9856             
9857             f.hide();
9858             
9859         }, this);
9860     },
9861     
9862     showFields : function(items)
9863     {
9864         Roo.each(items, function(i){
9865             
9866             var f = this.findField(i);
9867             
9868             if(!f){
9869                 return;
9870             }
9871             
9872             f.show();
9873             
9874         }, this);
9875     }
9876
9877 });
9878
9879 Roo.apply(Roo.bootstrap.Form, {
9880     
9881     popover : {
9882         
9883         padding : 5,
9884         
9885         isApplied : false,
9886         
9887         isMasked : false,
9888         
9889         form : false,
9890         
9891         target : false,
9892         
9893         toolTip : false,
9894         
9895         intervalID : false,
9896         
9897         maskEl : false,
9898         
9899         apply : function()
9900         {
9901             if(this.isApplied){
9902                 return;
9903             }
9904             
9905             this.maskEl = {
9906                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9907                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
9908                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
9909                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
9910             };
9911             
9912             this.maskEl.top.enableDisplayMode("block");
9913             this.maskEl.left.enableDisplayMode("block");
9914             this.maskEl.bottom.enableDisplayMode("block");
9915             this.maskEl.right.enableDisplayMode("block");
9916             
9917             this.toolTip = new Roo.bootstrap.Tooltip({
9918                 cls : 'roo-form-error-popover',
9919                 alignment : {
9920                     'left' : ['r-l', [-2,0], 'right'],
9921                     'right' : ['l-r', [2,0], 'left'],
9922                     'bottom' : ['tl-bl', [0,2], 'top'],
9923                     'top' : [ 'bl-tl', [0,-2], 'bottom']
9924                 }
9925             });
9926             
9927             this.toolTip.render(Roo.get(document.body));
9928
9929             this.toolTip.el.enableDisplayMode("block");
9930             
9931             Roo.get(document.body).on('click', function(){
9932                 this.unmask();
9933             }, this);
9934             
9935             Roo.get(document.body).on('touchstart', function(){
9936                 this.unmask();
9937             }, this);
9938             
9939             this.isApplied = true
9940         },
9941         
9942         mask : function(form, target)
9943         {
9944             this.form = form;
9945             
9946             this.target = target;
9947             
9948             if(!this.form.errorMask || !target.el){
9949                 return;
9950             }
9951             
9952             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9953             
9954             Roo.log(scrollable);
9955             
9956             var ot = this.target.el.calcOffsetsTo(scrollable);
9957             
9958             var scrollTo = ot[1] - this.form.maskOffset;
9959             
9960             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9961             
9962             scrollable.scrollTo('top', scrollTo);
9963             
9964             var box = this.target.el.getBox();
9965             Roo.log(box);
9966             var zIndex = Roo.bootstrap.Modal.zIndex++;
9967
9968             
9969             this.maskEl.top.setStyle('position', 'absolute');
9970             this.maskEl.top.setStyle('z-index', zIndex);
9971             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
9972             this.maskEl.top.setLeft(0);
9973             this.maskEl.top.setTop(0);
9974             this.maskEl.top.show();
9975             
9976             this.maskEl.left.setStyle('position', 'absolute');
9977             this.maskEl.left.setStyle('z-index', zIndex);
9978             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
9979             this.maskEl.left.setLeft(0);
9980             this.maskEl.left.setTop(box.y - this.padding);
9981             this.maskEl.left.show();
9982
9983             this.maskEl.bottom.setStyle('position', 'absolute');
9984             this.maskEl.bottom.setStyle('z-index', zIndex);
9985             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
9986             this.maskEl.bottom.setLeft(0);
9987             this.maskEl.bottom.setTop(box.bottom + this.padding);
9988             this.maskEl.bottom.show();
9989
9990             this.maskEl.right.setStyle('position', 'absolute');
9991             this.maskEl.right.setStyle('z-index', zIndex);
9992             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
9993             this.maskEl.right.setLeft(box.right + this.padding);
9994             this.maskEl.right.setTop(box.y - this.padding);
9995             this.maskEl.right.show();
9996
9997             this.toolTip.bindEl = this.target.el;
9998
9999             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10000
10001             var tip = this.target.blankText;
10002
10003             if(this.target.getValue() !== '' ) {
10004                 
10005                 if (this.target.invalidText.length) {
10006                     tip = this.target.invalidText;
10007                 } else if (this.target.regexText.length){
10008                     tip = this.target.regexText;
10009                 }
10010             }
10011
10012             this.toolTip.show(tip);
10013
10014             this.intervalID = window.setInterval(function() {
10015                 Roo.bootstrap.Form.popover.unmask();
10016             }, 10000);
10017
10018             window.onwheel = function(){ return false;};
10019             
10020             (function(){ this.isMasked = true; }).defer(500, this);
10021             
10022         },
10023         
10024         unmask : function()
10025         {
10026             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10027                 return;
10028             }
10029             
10030             this.maskEl.top.setStyle('position', 'absolute');
10031             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10032             this.maskEl.top.hide();
10033
10034             this.maskEl.left.setStyle('position', 'absolute');
10035             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10036             this.maskEl.left.hide();
10037
10038             this.maskEl.bottom.setStyle('position', 'absolute');
10039             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10040             this.maskEl.bottom.hide();
10041
10042             this.maskEl.right.setStyle('position', 'absolute');
10043             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10044             this.maskEl.right.hide();
10045             
10046             this.toolTip.hide();
10047             
10048             this.toolTip.el.hide();
10049             
10050             window.onwheel = function(){ return true;};
10051             
10052             if(this.intervalID){
10053                 window.clearInterval(this.intervalID);
10054                 this.intervalID = false;
10055             }
10056             
10057             this.isMasked = false;
10058             
10059         }
10060         
10061     }
10062     
10063 });
10064
10065 /*
10066  * Based on:
10067  * Ext JS Library 1.1.1
10068  * Copyright(c) 2006-2007, Ext JS, LLC.
10069  *
10070  * Originally Released Under LGPL - original licence link has changed is not relivant.
10071  *
10072  * Fork - LGPL
10073  * <script type="text/javascript">
10074  */
10075 /**
10076  * @class Roo.form.VTypes
10077  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10078  * @singleton
10079  */
10080 Roo.form.VTypes = function(){
10081     // closure these in so they are only created once.
10082     var alpha = /^[a-zA-Z_]+$/;
10083     var alphanum = /^[a-zA-Z0-9_]+$/;
10084     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10085     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10086
10087     // All these messages and functions are configurable
10088     return {
10089         /**
10090          * The function used to validate email addresses
10091          * @param {String} value The email address
10092          */
10093         'email' : function(v){
10094             return email.test(v);
10095         },
10096         /**
10097          * The error text to display when the email validation function returns false
10098          * @type String
10099          */
10100         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10101         /**
10102          * The keystroke filter mask to be applied on email input
10103          * @type RegExp
10104          */
10105         'emailMask' : /[a-z0-9_\.\-@]/i,
10106
10107         /**
10108          * The function used to validate URLs
10109          * @param {String} value The URL
10110          */
10111         'url' : function(v){
10112             return url.test(v);
10113         },
10114         /**
10115          * The error text to display when the url validation function returns false
10116          * @type String
10117          */
10118         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10119         
10120         /**
10121          * The function used to validate alpha values
10122          * @param {String} value The value
10123          */
10124         'alpha' : function(v){
10125             return alpha.test(v);
10126         },
10127         /**
10128          * The error text to display when the alpha validation function returns false
10129          * @type String
10130          */
10131         'alphaText' : 'This field should only contain letters and _',
10132         /**
10133          * The keystroke filter mask to be applied on alpha input
10134          * @type RegExp
10135          */
10136         'alphaMask' : /[a-z_]/i,
10137
10138         /**
10139          * The function used to validate alphanumeric values
10140          * @param {String} value The value
10141          */
10142         'alphanum' : function(v){
10143             return alphanum.test(v);
10144         },
10145         /**
10146          * The error text to display when the alphanumeric validation function returns false
10147          * @type String
10148          */
10149         'alphanumText' : 'This field should only contain letters, numbers and _',
10150         /**
10151          * The keystroke filter mask to be applied on alphanumeric input
10152          * @type RegExp
10153          */
10154         'alphanumMask' : /[a-z0-9_]/i
10155     };
10156 }();/*
10157  * - LGPL
10158  *
10159  * Input
10160  * 
10161  */
10162
10163 /**
10164  * @class Roo.bootstrap.Input
10165  * @extends Roo.bootstrap.Component
10166  * Bootstrap Input class
10167  * @cfg {Boolean} disabled is it disabled
10168  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
10169  * @cfg {String} name name of the input
10170  * @cfg {string} fieldLabel - the label associated
10171  * @cfg {string} placeholder - placeholder to put in text.
10172  * @cfg {string}  before - input group add on before
10173  * @cfg {string} after - input group add on after
10174  * @cfg {string} size - (lg|sm) or leave empty..
10175  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10176  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10177  * @cfg {Number} md colspan out of 12 for computer-sized screens
10178  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10179  * @cfg {string} value default value of the input
10180  * @cfg {Number} labelWidth set the width of label 
10181  * @cfg {Number} labellg set the width of label (1-12)
10182  * @cfg {Number} labelmd set the width of label (1-12)
10183  * @cfg {Number} labelsm set the width of label (1-12)
10184  * @cfg {Number} labelxs set the width of label (1-12)
10185  * @cfg {String} labelAlign (top|left)
10186  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10187  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10188  * @cfg {String} indicatorpos (left|right) default left
10189  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10190  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10191  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10192
10193  * @cfg {String} align (left|center|right) Default left
10194  * @cfg {Boolean} forceFeedback (true|false) Default false
10195  * 
10196  * @constructor
10197  * Create a new Input
10198  * @param {Object} config The config object
10199  */
10200
10201 Roo.bootstrap.Input = function(config){
10202     
10203     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10204     
10205     this.addEvents({
10206         /**
10207          * @event focus
10208          * Fires when this field receives input focus.
10209          * @param {Roo.form.Field} this
10210          */
10211         focus : true,
10212         /**
10213          * @event blur
10214          * Fires when this field loses input focus.
10215          * @param {Roo.form.Field} this
10216          */
10217         blur : true,
10218         /**
10219          * @event specialkey
10220          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10221          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10222          * @param {Roo.form.Field} this
10223          * @param {Roo.EventObject} e The event object
10224          */
10225         specialkey : true,
10226         /**
10227          * @event change
10228          * Fires just before the field blurs if the field value has changed.
10229          * @param {Roo.form.Field} this
10230          * @param {Mixed} newValue The new value
10231          * @param {Mixed} oldValue The original value
10232          */
10233         change : true,
10234         /**
10235          * @event invalid
10236          * Fires after the field has been marked as invalid.
10237          * @param {Roo.form.Field} this
10238          * @param {String} msg The validation message
10239          */
10240         invalid : true,
10241         /**
10242          * @event valid
10243          * Fires after the field has been validated with no errors.
10244          * @param {Roo.form.Field} this
10245          */
10246         valid : true,
10247          /**
10248          * @event keyup
10249          * Fires after the key up
10250          * @param {Roo.form.Field} this
10251          * @param {Roo.EventObject}  e The event Object
10252          */
10253         keyup : true
10254     });
10255 };
10256
10257 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10258      /**
10259      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10260       automatic validation (defaults to "keyup").
10261      */
10262     validationEvent : "keyup",
10263      /**
10264      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10265      */
10266     validateOnBlur : true,
10267     /**
10268      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10269      */
10270     validationDelay : 250,
10271      /**
10272      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10273      */
10274     focusClass : "x-form-focus",  // not needed???
10275     
10276        
10277     /**
10278      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10279      */
10280     invalidClass : "has-warning",
10281     
10282     /**
10283      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10284      */
10285     validClass : "has-success",
10286     
10287     /**
10288      * @cfg {Boolean} hasFeedback (true|false) default true
10289      */
10290     hasFeedback : true,
10291     
10292     /**
10293      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10294      */
10295     invalidFeedbackClass : "glyphicon-warning-sign",
10296     
10297     /**
10298      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10299      */
10300     validFeedbackClass : "glyphicon-ok",
10301     
10302     /**
10303      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10304      */
10305     selectOnFocus : false,
10306     
10307      /**
10308      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10309      */
10310     maskRe : null,
10311        /**
10312      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10313      */
10314     vtype : null,
10315     
10316       /**
10317      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10318      */
10319     disableKeyFilter : false,
10320     
10321        /**
10322      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10323      */
10324     disabled : false,
10325      /**
10326      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10327      */
10328     allowBlank : true,
10329     /**
10330      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10331      */
10332     blankText : "Please complete this mandatory field",
10333     
10334      /**
10335      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10336      */
10337     minLength : 0,
10338     /**
10339      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10340      */
10341     maxLength : Number.MAX_VALUE,
10342     /**
10343      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10344      */
10345     minLengthText : "The minimum length for this field is {0}",
10346     /**
10347      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10348      */
10349     maxLengthText : "The maximum length for this field is {0}",
10350   
10351     
10352     /**
10353      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10354      * If available, this function will be called only after the basic validators all return true, and will be passed the
10355      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10356      */
10357     validator : null,
10358     /**
10359      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10360      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10361      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10362      */
10363     regex : null,
10364     /**
10365      * @cfg {String} regexText -- Depricated - use Invalid Text
10366      */
10367     regexText : "",
10368     
10369     /**
10370      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10371      */
10372     invalidText : "",
10373     
10374     
10375     
10376     autocomplete: false,
10377     
10378     
10379     fieldLabel : '',
10380     inputType : 'text',
10381     
10382     name : false,
10383     placeholder: false,
10384     before : false,
10385     after : false,
10386     size : false,
10387     hasFocus : false,
10388     preventMark: false,
10389     isFormField : true,
10390     value : '',
10391     labelWidth : 2,
10392     labelAlign : false,
10393     readOnly : false,
10394     align : false,
10395     formatedValue : false,
10396     forceFeedback : false,
10397     
10398     indicatorpos : 'left',
10399     
10400     labellg : 0,
10401     labelmd : 0,
10402     labelsm : 0,
10403     labelxs : 0,
10404     
10405     capture : '',
10406     accept : '',
10407     
10408     parentLabelAlign : function()
10409     {
10410         var parent = this;
10411         while (parent.parent()) {
10412             parent = parent.parent();
10413             if (typeof(parent.labelAlign) !='undefined') {
10414                 return parent.labelAlign;
10415             }
10416         }
10417         return 'left';
10418         
10419     },
10420     
10421     getAutoCreate : function()
10422     {
10423         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10424         
10425         var id = Roo.id();
10426         
10427         var cfg = {};
10428         
10429         if(this.inputType != 'hidden'){
10430             cfg.cls = 'form-group' //input-group
10431         }
10432         
10433         var input =  {
10434             tag: 'input',
10435             id : id,
10436             type : this.inputType,
10437             value : this.value,
10438             cls : 'form-control',
10439             placeholder : this.placeholder || '',
10440             autocomplete : this.autocomplete || 'new-password'
10441         };
10442         
10443         if(this.capture.length){
10444             input.capture = this.capture;
10445         }
10446         
10447         if(this.accept.length){
10448             input.accept = this.accept + "/*";
10449         }
10450         
10451         if(this.align){
10452             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10453         }
10454         
10455         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10456             input.maxLength = this.maxLength;
10457         }
10458         
10459         if (this.disabled) {
10460             input.disabled=true;
10461         }
10462         
10463         if (this.readOnly) {
10464             input.readonly=true;
10465         }
10466         
10467         if (this.name) {
10468             input.name = this.name;
10469         }
10470         
10471         if (this.size) {
10472             input.cls += ' input-' + this.size;
10473         }
10474         
10475         var settings=this;
10476         ['xs','sm','md','lg'].map(function(size){
10477             if (settings[size]) {
10478                 cfg.cls += ' col-' + size + '-' + settings[size];
10479             }
10480         });
10481         
10482         var inputblock = input;
10483         
10484         var feedback = {
10485             tag: 'span',
10486             cls: 'glyphicon form-control-feedback'
10487         };
10488             
10489         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10490             
10491             inputblock = {
10492                 cls : 'has-feedback',
10493                 cn :  [
10494                     input,
10495                     feedback
10496                 ] 
10497             };  
10498         }
10499         
10500         if (this.before || this.after) {
10501             
10502             inputblock = {
10503                 cls : 'input-group',
10504                 cn :  [] 
10505             };
10506             
10507             if (this.before && typeof(this.before) == 'string') {
10508                 
10509                 inputblock.cn.push({
10510                     tag :'span',
10511                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10512                     html : this.before
10513                 });
10514             }
10515             if (this.before && typeof(this.before) == 'object') {
10516                 this.before = Roo.factory(this.before);
10517                 
10518                 inputblock.cn.push({
10519                     tag :'span',
10520                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
10521                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10522                 });
10523             }
10524             
10525             inputblock.cn.push(input);
10526             
10527             if (this.after && typeof(this.after) == 'string') {
10528                 inputblock.cn.push({
10529                     tag :'span',
10530                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10531                     html : this.after
10532                 });
10533             }
10534             if (this.after && typeof(this.after) == 'object') {
10535                 this.after = Roo.factory(this.after);
10536                 
10537                 inputblock.cn.push({
10538                     tag :'span',
10539                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
10540                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10541                 });
10542             }
10543             
10544             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10545                 inputblock.cls += ' has-feedback';
10546                 inputblock.cn.push(feedback);
10547             }
10548         };
10549         var indicator = {
10550             tag : 'i',
10551             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10552             tooltip : 'This field is required'
10553         };
10554         if (Roo.bootstrap.version == 4) {
10555             indicator = {
10556                 tag : 'i',
10557                 style : 'display-none'
10558             };
10559         }
10560         if (align ==='left' && this.fieldLabel.length) {
10561             
10562             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10563             
10564             cfg.cn = [
10565                 indicator,
10566                 {
10567                     tag: 'label',
10568                     'for' :  id,
10569                     cls : 'control-label col-form-label',
10570                     html : this.fieldLabel
10571
10572                 },
10573                 {
10574                     cls : "", 
10575                     cn: [
10576                         inputblock
10577                     ]
10578                 }
10579             ];
10580             
10581             var labelCfg = cfg.cn[1];
10582             var contentCfg = cfg.cn[2];
10583             
10584             if(this.indicatorpos == 'right'){
10585                 cfg.cn = [
10586                     {
10587                         tag: 'label',
10588                         'for' :  id,
10589                         cls : 'control-label col-form-label',
10590                         cn : [
10591                             {
10592                                 tag : 'span',
10593                                 html : this.fieldLabel
10594                             },
10595                             indicator
10596                         ]
10597                     },
10598                     {
10599                         cls : "",
10600                         cn: [
10601                             inputblock
10602                         ]
10603                     }
10604
10605                 ];
10606                 
10607                 labelCfg = cfg.cn[0];
10608                 contentCfg = cfg.cn[1];
10609             
10610             }
10611             
10612             if(this.labelWidth > 12){
10613                 labelCfg.style = "width: " + this.labelWidth + 'px';
10614             }
10615             
10616             if(this.labelWidth < 13 && this.labelmd == 0){
10617                 this.labelmd = this.labelWidth;
10618             }
10619             
10620             if(this.labellg > 0){
10621                 labelCfg.cls += ' col-lg-' + this.labellg;
10622                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10623             }
10624             
10625             if(this.labelmd > 0){
10626                 labelCfg.cls += ' col-md-' + this.labelmd;
10627                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10628             }
10629             
10630             if(this.labelsm > 0){
10631                 labelCfg.cls += ' col-sm-' + this.labelsm;
10632                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10633             }
10634             
10635             if(this.labelxs > 0){
10636                 labelCfg.cls += ' col-xs-' + this.labelxs;
10637                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10638             }
10639             
10640             
10641         } else if ( this.fieldLabel.length) {
10642                 
10643             cfg.cn = [
10644                 {
10645                     tag : 'i',
10646                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10647                     tooltip : 'This field is required'
10648                 },
10649                 {
10650                     tag: 'label',
10651                    //cls : 'input-group-addon',
10652                     html : this.fieldLabel
10653
10654                 },
10655
10656                inputblock
10657
10658            ];
10659            
10660            if(this.indicatorpos == 'right'){
10661                 
10662                 cfg.cn = [
10663                     {
10664                         tag: 'label',
10665                        //cls : 'input-group-addon',
10666                         html : this.fieldLabel
10667
10668                     },
10669                     {
10670                         tag : 'i',
10671                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10672                         tooltip : 'This field is required'
10673                     },
10674
10675                    inputblock
10676
10677                ];
10678
10679             }
10680
10681         } else {
10682             
10683             cfg.cn = [
10684
10685                     inputblock
10686
10687             ];
10688                 
10689                 
10690         };
10691         
10692         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10693            cfg.cls += ' navbar-form';
10694         }
10695         
10696         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10697             // on BS4 we do this only if not form 
10698             cfg.cls += ' navbar-form';
10699             cfg.tag = 'li';
10700         }
10701         
10702         return cfg;
10703         
10704     },
10705     /**
10706      * return the real input element.
10707      */
10708     inputEl: function ()
10709     {
10710         return this.el.select('input.form-control',true).first();
10711     },
10712     
10713     tooltipEl : function()
10714     {
10715         return this.inputEl();
10716     },
10717     
10718     indicatorEl : function()
10719     {
10720         if (Roo.bootstrap.version == 4) {
10721             return false; // not enabled in v4 yet.
10722         }
10723         
10724         var indicator = this.el.select('i.roo-required-indicator',true).first();
10725         
10726         if(!indicator){
10727             return false;
10728         }
10729         
10730         return indicator;
10731         
10732     },
10733     
10734     setDisabled : function(v)
10735     {
10736         var i  = this.inputEl().dom;
10737         if (!v) {
10738             i.removeAttribute('disabled');
10739             return;
10740             
10741         }
10742         i.setAttribute('disabled','true');
10743     },
10744     initEvents : function()
10745     {
10746           
10747         this.inputEl().on("keydown" , this.fireKey,  this);
10748         this.inputEl().on("focus", this.onFocus,  this);
10749         this.inputEl().on("blur", this.onBlur,  this);
10750         
10751         this.inputEl().relayEvent('keyup', this);
10752         
10753         this.indicator = this.indicatorEl();
10754         
10755         if(this.indicator){
10756             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
10757         }
10758  
10759         // reference to original value for reset
10760         this.originalValue = this.getValue();
10761         //Roo.form.TextField.superclass.initEvents.call(this);
10762         if(this.validationEvent == 'keyup'){
10763             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10764             this.inputEl().on('keyup', this.filterValidation, this);
10765         }
10766         else if(this.validationEvent !== false){
10767             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10768         }
10769         
10770         if(this.selectOnFocus){
10771             this.on("focus", this.preFocus, this);
10772             
10773         }
10774         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10775             this.inputEl().on("keypress", this.filterKeys, this);
10776         } else {
10777             this.inputEl().relayEvent('keypress', this);
10778         }
10779        /* if(this.grow){
10780             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
10781             this.el.on("click", this.autoSize,  this);
10782         }
10783         */
10784         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10785             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10786         }
10787         
10788         if (typeof(this.before) == 'object') {
10789             this.before.render(this.el.select('.roo-input-before',true).first());
10790         }
10791         if (typeof(this.after) == 'object') {
10792             this.after.render(this.el.select('.roo-input-after',true).first());
10793         }
10794         
10795         this.inputEl().on('change', this.onChange, this);
10796         
10797     },
10798     filterValidation : function(e){
10799         if(!e.isNavKeyPress()){
10800             this.validationTask.delay(this.validationDelay);
10801         }
10802     },
10803      /**
10804      * Validates the field value
10805      * @return {Boolean} True if the value is valid, else false
10806      */
10807     validate : function(){
10808         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10809         if(this.disabled || this.validateValue(this.getRawValue())){
10810             this.markValid();
10811             return true;
10812         }
10813         
10814         this.markInvalid();
10815         return false;
10816     },
10817     
10818     
10819     /**
10820      * Validates a value according to the field's validation rules and marks the field as invalid
10821      * if the validation fails
10822      * @param {Mixed} value The value to validate
10823      * @return {Boolean} True if the value is valid, else false
10824      */
10825     validateValue : function(value)
10826     {
10827         if(this.getVisibilityEl().hasClass('hidden')){
10828             return true;
10829         }
10830         
10831         if(value.length < 1)  { // if it's blank
10832             if(this.allowBlank){
10833                 return true;
10834             }
10835             return false;
10836         }
10837         
10838         if(value.length < this.minLength){
10839             return false;
10840         }
10841         if(value.length > this.maxLength){
10842             return false;
10843         }
10844         if(this.vtype){
10845             var vt = Roo.form.VTypes;
10846             if(!vt[this.vtype](value, this)){
10847                 return false;
10848             }
10849         }
10850         if(typeof this.validator == "function"){
10851             var msg = this.validator(value);
10852             if(msg !== true){
10853                 return false;
10854             }
10855             if (typeof(msg) == 'string') {
10856                 this.invalidText = msg;
10857             }
10858         }
10859         
10860         if(this.regex && !this.regex.test(value)){
10861             return false;
10862         }
10863         
10864         return true;
10865     },
10866     
10867      // private
10868     fireKey : function(e){
10869         //Roo.log('field ' + e.getKey());
10870         if(e.isNavKeyPress()){
10871             this.fireEvent("specialkey", this, e);
10872         }
10873     },
10874     focus : function (selectText){
10875         if(this.rendered){
10876             this.inputEl().focus();
10877             if(selectText === true){
10878                 this.inputEl().dom.select();
10879             }
10880         }
10881         return this;
10882     } ,
10883     
10884     onFocus : function(){
10885         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10886            // this.el.addClass(this.focusClass);
10887         }
10888         if(!this.hasFocus){
10889             this.hasFocus = true;
10890             this.startValue = this.getValue();
10891             this.fireEvent("focus", this);
10892         }
10893     },
10894     
10895     beforeBlur : Roo.emptyFn,
10896
10897     
10898     // private
10899     onBlur : function(){
10900         this.beforeBlur();
10901         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10902             //this.el.removeClass(this.focusClass);
10903         }
10904         this.hasFocus = false;
10905         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
10906             this.validate();
10907         }
10908         var v = this.getValue();
10909         if(String(v) !== String(this.startValue)){
10910             this.fireEvent('change', this, v, this.startValue);
10911         }
10912         this.fireEvent("blur", this);
10913     },
10914     
10915     onChange : function(e)
10916     {
10917         var v = this.getValue();
10918         if(String(v) !== String(this.startValue)){
10919             this.fireEvent('change', this, v, this.startValue);
10920         }
10921         
10922     },
10923     
10924     /**
10925      * Resets the current field value to the originally loaded value and clears any validation messages
10926      */
10927     reset : function(){
10928         this.setValue(this.originalValue);
10929         this.validate();
10930     },
10931      /**
10932      * Returns the name of the field
10933      * @return {Mixed} name The name field
10934      */
10935     getName: function(){
10936         return this.name;
10937     },
10938      /**
10939      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
10940      * @return {Mixed} value The field value
10941      */
10942     getValue : function(){
10943         
10944         var v = this.inputEl().getValue();
10945         
10946         return v;
10947     },
10948     /**
10949      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
10950      * @return {Mixed} value The field value
10951      */
10952     getRawValue : function(){
10953         var v = this.inputEl().getValue();
10954         
10955         return v;
10956     },
10957     
10958     /**
10959      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
10960      * @param {Mixed} value The value to set
10961      */
10962     setRawValue : function(v){
10963         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10964     },
10965     
10966     selectText : function(start, end){
10967         var v = this.getRawValue();
10968         if(v.length > 0){
10969             start = start === undefined ? 0 : start;
10970             end = end === undefined ? v.length : end;
10971             var d = this.inputEl().dom;
10972             if(d.setSelectionRange){
10973                 d.setSelectionRange(start, end);
10974             }else if(d.createTextRange){
10975                 var range = d.createTextRange();
10976                 range.moveStart("character", start);
10977                 range.moveEnd("character", v.length-end);
10978                 range.select();
10979             }
10980         }
10981     },
10982     
10983     /**
10984      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
10985      * @param {Mixed} value The value to set
10986      */
10987     setValue : function(v){
10988         this.value = v;
10989         if(this.rendered){
10990             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10991             this.validate();
10992         }
10993     },
10994     
10995     /*
10996     processValue : function(value){
10997         if(this.stripCharsRe){
10998             var newValue = value.replace(this.stripCharsRe, '');
10999             if(newValue !== value){
11000                 this.setRawValue(newValue);
11001                 return newValue;
11002             }
11003         }
11004         return value;
11005     },
11006   */
11007     preFocus : function(){
11008         
11009         if(this.selectOnFocus){
11010             this.inputEl().dom.select();
11011         }
11012     },
11013     filterKeys : function(e){
11014         var k = e.getKey();
11015         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11016             return;
11017         }
11018         var c = e.getCharCode(), cc = String.fromCharCode(c);
11019         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11020             return;
11021         }
11022         if(!this.maskRe.test(cc)){
11023             e.stopEvent();
11024         }
11025     },
11026      /**
11027      * Clear any invalid styles/messages for this field
11028      */
11029     clearInvalid : function(){
11030         
11031         if(!this.el || this.preventMark){ // not rendered
11032             return;
11033         }
11034         
11035         
11036         this.el.removeClass([this.invalidClass, 'is-invalid']);
11037         
11038         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11039             
11040             var feedback = this.el.select('.form-control-feedback', true).first();
11041             
11042             if(feedback){
11043                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11044             }
11045             
11046         }
11047         
11048         if(this.indicator){
11049             this.indicator.removeClass('visible');
11050             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11051         }
11052         
11053         this.fireEvent('valid', this);
11054     },
11055     
11056      /**
11057      * Mark this field as valid
11058      */
11059     markValid : function()
11060     {
11061         if(!this.el  || this.preventMark){ // not rendered...
11062             return;
11063         }
11064         
11065         this.el.removeClass([this.invalidClass, this.validClass]);
11066         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11067
11068         var feedback = this.el.select('.form-control-feedback', true).first();
11069             
11070         if(feedback){
11071             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11072         }
11073         
11074         if(this.indicator){
11075             this.indicator.removeClass('visible');
11076             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11077         }
11078         
11079         if(this.disabled){
11080             return;
11081         }
11082         
11083            
11084         if(this.allowBlank && !this.getRawValue().length){
11085             return;
11086         }
11087         if (Roo.bootstrap.version == 3) {
11088             this.el.addClass(this.validClass);
11089         } else {
11090             this.inputEl().addClass('is-valid');
11091         }
11092
11093         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11094             
11095             var feedback = this.el.select('.form-control-feedback', true).first();
11096             
11097             if(feedback){
11098                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11099                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11100             }
11101             
11102         }
11103         
11104         this.fireEvent('valid', this);
11105     },
11106     
11107      /**
11108      * Mark this field as invalid
11109      * @param {String} msg The validation message
11110      */
11111     markInvalid : function(msg)
11112     {
11113         if(!this.el  || this.preventMark){ // not rendered
11114             return;
11115         }
11116         
11117         this.el.removeClass([this.invalidClass, this.validClass]);
11118         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11119         
11120         var feedback = this.el.select('.form-control-feedback', true).first();
11121             
11122         if(feedback){
11123             this.el.select('.form-control-feedback', true).first().removeClass(
11124                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11125         }
11126
11127         if(this.disabled){
11128             return;
11129         }
11130         
11131         if(this.allowBlank && !this.getRawValue().length){
11132             return;
11133         }
11134         
11135         if(this.indicator){
11136             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11137             this.indicator.addClass('visible');
11138         }
11139         if (Roo.bootstrap.version == 3) {
11140             this.el.addClass(this.invalidClass);
11141         } else {
11142             this.inputEl().addClass('is-invalid');
11143         }
11144         
11145         
11146         
11147         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11148             
11149             var feedback = this.el.select('.form-control-feedback', true).first();
11150             
11151             if(feedback){
11152                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11153                 
11154                 if(this.getValue().length || this.forceFeedback){
11155                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11156                 }
11157                 
11158             }
11159             
11160         }
11161         
11162         this.fireEvent('invalid', this, msg);
11163     },
11164     // private
11165     SafariOnKeyDown : function(event)
11166     {
11167         // this is a workaround for a password hang bug on chrome/ webkit.
11168         if (this.inputEl().dom.type != 'password') {
11169             return;
11170         }
11171         
11172         var isSelectAll = false;
11173         
11174         if(this.inputEl().dom.selectionEnd > 0){
11175             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11176         }
11177         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11178             event.preventDefault();
11179             this.setValue('');
11180             return;
11181         }
11182         
11183         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11184             
11185             event.preventDefault();
11186             // this is very hacky as keydown always get's upper case.
11187             //
11188             var cc = String.fromCharCode(event.getCharCode());
11189             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11190             
11191         }
11192     },
11193     adjustWidth : function(tag, w){
11194         tag = tag.toLowerCase();
11195         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11196             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11197                 if(tag == 'input'){
11198                     return w + 2;
11199                 }
11200                 if(tag == 'textarea'){
11201                     return w-2;
11202                 }
11203             }else if(Roo.isOpera){
11204                 if(tag == 'input'){
11205                     return w + 2;
11206                 }
11207                 if(tag == 'textarea'){
11208                     return w-2;
11209                 }
11210             }
11211         }
11212         return w;
11213     },
11214     
11215     setFieldLabel : function(v)
11216     {
11217         if(!this.rendered){
11218             return;
11219         }
11220         
11221         if(this.indicatorEl()){
11222             var ar = this.el.select('label > span',true);
11223             
11224             if (ar.elements.length) {
11225                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11226                 this.fieldLabel = v;
11227                 return;
11228             }
11229             
11230             var br = this.el.select('label',true);
11231             
11232             if(br.elements.length) {
11233                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11234                 this.fieldLabel = v;
11235                 return;
11236             }
11237             
11238             Roo.log('Cannot Found any of label > span || label in input');
11239             return;
11240         }
11241         
11242         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11243         this.fieldLabel = v;
11244         
11245         
11246     }
11247 });
11248
11249  
11250 /*
11251  * - LGPL
11252  *
11253  * Input
11254  * 
11255  */
11256
11257 /**
11258  * @class Roo.bootstrap.TextArea
11259  * @extends Roo.bootstrap.Input
11260  * Bootstrap TextArea class
11261  * @cfg {Number} cols Specifies the visible width of a text area
11262  * @cfg {Number} rows Specifies the visible number of lines in a text area
11263  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11264  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11265  * @cfg {string} html text
11266  * 
11267  * @constructor
11268  * Create a new TextArea
11269  * @param {Object} config The config object
11270  */
11271
11272 Roo.bootstrap.TextArea = function(config){
11273     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11274    
11275 };
11276
11277 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11278      
11279     cols : false,
11280     rows : 5,
11281     readOnly : false,
11282     warp : 'soft',
11283     resize : false,
11284     value: false,
11285     html: false,
11286     
11287     getAutoCreate : function(){
11288         
11289         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11290         
11291         var id = Roo.id();
11292         
11293         var cfg = {};
11294         
11295         if(this.inputType != 'hidden'){
11296             cfg.cls = 'form-group' //input-group
11297         }
11298         
11299         var input =  {
11300             tag: 'textarea',
11301             id : id,
11302             warp : this.warp,
11303             rows : this.rows,
11304             value : this.value || '',
11305             html: this.html || '',
11306             cls : 'form-control',
11307             placeholder : this.placeholder || '' 
11308             
11309         };
11310         
11311         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11312             input.maxLength = this.maxLength;
11313         }
11314         
11315         if(this.resize){
11316             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11317         }
11318         
11319         if(this.cols){
11320             input.cols = this.cols;
11321         }
11322         
11323         if (this.readOnly) {
11324             input.readonly = true;
11325         }
11326         
11327         if (this.name) {
11328             input.name = this.name;
11329         }
11330         
11331         if (this.size) {
11332             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11333         }
11334         
11335         var settings=this;
11336         ['xs','sm','md','lg'].map(function(size){
11337             if (settings[size]) {
11338                 cfg.cls += ' col-' + size + '-' + settings[size];
11339             }
11340         });
11341         
11342         var inputblock = input;
11343         
11344         if(this.hasFeedback && !this.allowBlank){
11345             
11346             var feedback = {
11347                 tag: 'span',
11348                 cls: 'glyphicon form-control-feedback'
11349             };
11350
11351             inputblock = {
11352                 cls : 'has-feedback',
11353                 cn :  [
11354                     input,
11355                     feedback
11356                 ] 
11357             };  
11358         }
11359         
11360         
11361         if (this.before || this.after) {
11362             
11363             inputblock = {
11364                 cls : 'input-group',
11365                 cn :  [] 
11366             };
11367             if (this.before) {
11368                 inputblock.cn.push({
11369                     tag :'span',
11370                     cls : 'input-group-addon',
11371                     html : this.before
11372                 });
11373             }
11374             
11375             inputblock.cn.push(input);
11376             
11377             if(this.hasFeedback && !this.allowBlank){
11378                 inputblock.cls += ' has-feedback';
11379                 inputblock.cn.push(feedback);
11380             }
11381             
11382             if (this.after) {
11383                 inputblock.cn.push({
11384                     tag :'span',
11385                     cls : 'input-group-addon',
11386                     html : this.after
11387                 });
11388             }
11389             
11390         }
11391         
11392         if (align ==='left' && this.fieldLabel.length) {
11393             cfg.cn = [
11394                 {
11395                     tag: 'label',
11396                     'for' :  id,
11397                     cls : 'control-label',
11398                     html : this.fieldLabel
11399                 },
11400                 {
11401                     cls : "",
11402                     cn: [
11403                         inputblock
11404                     ]
11405                 }
11406
11407             ];
11408             
11409             if(this.labelWidth > 12){
11410                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11411             }
11412
11413             if(this.labelWidth < 13 && this.labelmd == 0){
11414                 this.labelmd = this.labelWidth;
11415             }
11416
11417             if(this.labellg > 0){
11418                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11419                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11420             }
11421
11422             if(this.labelmd > 0){
11423                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11424                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11425             }
11426
11427             if(this.labelsm > 0){
11428                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11429                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11430             }
11431
11432             if(this.labelxs > 0){
11433                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11434                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11435             }
11436             
11437         } else if ( this.fieldLabel.length) {
11438             cfg.cn = [
11439
11440                {
11441                    tag: 'label',
11442                    //cls : 'input-group-addon',
11443                    html : this.fieldLabel
11444
11445                },
11446
11447                inputblock
11448
11449            ];
11450
11451         } else {
11452
11453             cfg.cn = [
11454
11455                 inputblock
11456
11457             ];
11458                 
11459         }
11460         
11461         if (this.disabled) {
11462             input.disabled=true;
11463         }
11464         
11465         return cfg;
11466         
11467     },
11468     /**
11469      * return the real textarea element.
11470      */
11471     inputEl: function ()
11472     {
11473         return this.el.select('textarea.form-control',true).first();
11474     },
11475     
11476     /**
11477      * Clear any invalid styles/messages for this field
11478      */
11479     clearInvalid : function()
11480     {
11481         
11482         if(!this.el || this.preventMark){ // not rendered
11483             return;
11484         }
11485         
11486         var label = this.el.select('label', true).first();
11487         var icon = this.el.select('i.fa-star', true).first();
11488         
11489         if(label && icon){
11490             icon.remove();
11491         }
11492         this.el.removeClass( this.validClass);
11493         this.inputEl().removeClass('is-invalid');
11494          
11495         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11496             
11497             var feedback = this.el.select('.form-control-feedback', true).first();
11498             
11499             if(feedback){
11500                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11501             }
11502             
11503         }
11504         
11505         this.fireEvent('valid', this);
11506     },
11507     
11508      /**
11509      * Mark this field as valid
11510      */
11511     markValid : function()
11512     {
11513         if(!this.el  || this.preventMark){ // not rendered
11514             return;
11515         }
11516         
11517         this.el.removeClass([this.invalidClass, this.validClass]);
11518         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11519         
11520         var feedback = this.el.select('.form-control-feedback', true).first();
11521             
11522         if(feedback){
11523             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11524         }
11525
11526         if(this.disabled || this.allowBlank){
11527             return;
11528         }
11529         
11530         var label = this.el.select('label', true).first();
11531         var icon = this.el.select('i.fa-star', true).first();
11532         
11533         if(label && icon){
11534             icon.remove();
11535         }
11536         if (Roo.bootstrap.version == 3) {
11537             this.el.addClass(this.validClass);
11538         } else {
11539             this.inputEl().addClass('is-valid');
11540         }
11541         
11542         
11543         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11544             
11545             var feedback = this.el.select('.form-control-feedback', true).first();
11546             
11547             if(feedback){
11548                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11549                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11550             }
11551             
11552         }
11553         
11554         this.fireEvent('valid', this);
11555     },
11556     
11557      /**
11558      * Mark this field as invalid
11559      * @param {String} msg The validation message
11560      */
11561     markInvalid : function(msg)
11562     {
11563         if(!this.el  || this.preventMark){ // not rendered
11564             return;
11565         }
11566         
11567         this.el.removeClass([this.invalidClass, this.validClass]);
11568         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11569         
11570         var feedback = this.el.select('.form-control-feedback', true).first();
11571             
11572         if(feedback){
11573             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11574         }
11575
11576         if(this.disabled || this.allowBlank){
11577             return;
11578         }
11579         
11580         var label = this.el.select('label', true).first();
11581         var icon = this.el.select('i.fa-star', true).first();
11582         
11583         if(!this.getValue().length && label && !icon){
11584             this.el.createChild({
11585                 tag : 'i',
11586                 cls : 'text-danger fa fa-lg fa-star',
11587                 tooltip : 'This field is required',
11588                 style : 'margin-right:5px;'
11589             }, label, true);
11590         }
11591         
11592         if (Roo.bootstrap.version == 3) {
11593             this.el.addClass(this.invalidClass);
11594         } else {
11595             this.inputEl().addClass('is-invalid');
11596         }
11597         
11598         // fixme ... this may be depricated need to test..
11599         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11600             
11601             var feedback = this.el.select('.form-control-feedback', true).first();
11602             
11603             if(feedback){
11604                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11605                 
11606                 if(this.getValue().length || this.forceFeedback){
11607                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11608                 }
11609                 
11610             }
11611             
11612         }
11613         
11614         this.fireEvent('invalid', this, msg);
11615     }
11616 });
11617
11618  
11619 /*
11620  * - LGPL
11621  *
11622  * trigger field - base class for combo..
11623  * 
11624  */
11625  
11626 /**
11627  * @class Roo.bootstrap.TriggerField
11628  * @extends Roo.bootstrap.Input
11629  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11630  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11631  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11632  * for which you can provide a custom implementation.  For example:
11633  * <pre><code>
11634 var trigger = new Roo.bootstrap.TriggerField();
11635 trigger.onTriggerClick = myTriggerFn;
11636 trigger.applyTo('my-field');
11637 </code></pre>
11638  *
11639  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11640  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11641  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11642  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11643  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11644
11645  * @constructor
11646  * Create a new TriggerField.
11647  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11648  * to the base TextField)
11649  */
11650 Roo.bootstrap.TriggerField = function(config){
11651     this.mimicing = false;
11652     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11653 };
11654
11655 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11656     /**
11657      * @cfg {String} triggerClass A CSS class to apply to the trigger
11658      */
11659      /**
11660      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11661      */
11662     hideTrigger:false,
11663
11664     /**
11665      * @cfg {Boolean} removable (true|false) special filter default false
11666      */
11667     removable : false,
11668     
11669     /** @cfg {Boolean} grow @hide */
11670     /** @cfg {Number} growMin @hide */
11671     /** @cfg {Number} growMax @hide */
11672
11673     /**
11674      * @hide 
11675      * @method
11676      */
11677     autoSize: Roo.emptyFn,
11678     // private
11679     monitorTab : true,
11680     // private
11681     deferHeight : true,
11682
11683     
11684     actionMode : 'wrap',
11685     
11686     caret : false,
11687     
11688     
11689     getAutoCreate : function(){
11690        
11691         var align = this.labelAlign || this.parentLabelAlign();
11692         
11693         var id = Roo.id();
11694         
11695         var cfg = {
11696             cls: 'form-group' //input-group
11697         };
11698         
11699         
11700         var input =  {
11701             tag: 'input',
11702             id : id,
11703             type : this.inputType,
11704             cls : 'form-control',
11705             autocomplete: 'new-password',
11706             placeholder : this.placeholder || '' 
11707             
11708         };
11709         if (this.name) {
11710             input.name = this.name;
11711         }
11712         if (this.size) {
11713             input.cls += ' input-' + this.size;
11714         }
11715         
11716         if (this.disabled) {
11717             input.disabled=true;
11718         }
11719         
11720         var inputblock = input;
11721         
11722         if(this.hasFeedback && !this.allowBlank){
11723             
11724             var feedback = {
11725                 tag: 'span',
11726                 cls: 'glyphicon form-control-feedback'
11727             };
11728             
11729             if(this.removable && !this.editable  ){
11730                 inputblock = {
11731                     cls : 'has-feedback',
11732                     cn :  [
11733                         inputblock,
11734                         {
11735                             tag: 'button',
11736                             html : 'x',
11737                             cls : 'roo-combo-removable-btn close'
11738                         },
11739                         feedback
11740                     ] 
11741                 };
11742             } else {
11743                 inputblock = {
11744                     cls : 'has-feedback',
11745                     cn :  [
11746                         inputblock,
11747                         feedback
11748                     ] 
11749                 };
11750             }
11751
11752         } else {
11753             if(this.removable && !this.editable ){
11754                 inputblock = {
11755                     cls : 'roo-removable',
11756                     cn :  [
11757                         inputblock,
11758                         {
11759                             tag: 'button',
11760                             html : 'x',
11761                             cls : 'roo-combo-removable-btn close'
11762                         }
11763                     ] 
11764                 };
11765             }
11766         }
11767         
11768         if (this.before || this.after) {
11769             
11770             inputblock = {
11771                 cls : 'input-group',
11772                 cn :  [] 
11773             };
11774             if (this.before) {
11775                 inputblock.cn.push({
11776                     tag :'span',
11777                     cls : 'input-group-addon input-group-prepend input-group-text',
11778                     html : this.before
11779                 });
11780             }
11781             
11782             inputblock.cn.push(input);
11783             
11784             if(this.hasFeedback && !this.allowBlank){
11785                 inputblock.cls += ' has-feedback';
11786                 inputblock.cn.push(feedback);
11787             }
11788             
11789             if (this.after) {
11790                 inputblock.cn.push({
11791                     tag :'span',
11792                     cls : 'input-group-addon input-group-append input-group-text',
11793                     html : this.after
11794                 });
11795             }
11796             
11797         };
11798         
11799       
11800         
11801         var ibwrap = inputblock;
11802         
11803         if(this.multiple){
11804             ibwrap = {
11805                 tag: 'ul',
11806                 cls: 'roo-select2-choices',
11807                 cn:[
11808                     {
11809                         tag: 'li',
11810                         cls: 'roo-select2-search-field',
11811                         cn: [
11812
11813                             inputblock
11814                         ]
11815                     }
11816                 ]
11817             };
11818                 
11819         }
11820         
11821         var combobox = {
11822             cls: 'roo-select2-container input-group',
11823             cn: [
11824                  {
11825                     tag: 'input',
11826                     type : 'hidden',
11827                     cls: 'form-hidden-field'
11828                 },
11829                 ibwrap
11830             ]
11831         };
11832         
11833         if(!this.multiple && this.showToggleBtn){
11834             
11835             var caret = {
11836                         tag: 'span',
11837                         cls: 'caret'
11838              };
11839             if (this.caret != false) {
11840                 caret = {
11841                      tag: 'i',
11842                      cls: 'fa fa-' + this.caret
11843                 };
11844                 
11845             }
11846             
11847             combobox.cn.push({
11848                 tag :'span',
11849                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11850                 cn : [
11851                     Roo.bootstrap.version == 3 ? caret : '',
11852                     {
11853                         tag: 'span',
11854                         cls: 'combobox-clear',
11855                         cn  : [
11856                             {
11857                                 tag : 'i',
11858                                 cls: 'icon-remove'
11859                             }
11860                         ]
11861                     }
11862                 ]
11863
11864             })
11865         }
11866         
11867         if(this.multiple){
11868             combobox.cls += ' roo-select2-container-multi';
11869         }
11870          var indicator = {
11871             tag : 'i',
11872             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11873             tooltip : 'This field is required'
11874         };
11875         if (Roo.bootstrap.version == 4) {
11876             indicator = {
11877                 tag : 'i',
11878                 style : 'display:none'
11879             };
11880         }
11881         
11882         
11883         if (align ==='left' && this.fieldLabel.length) {
11884             
11885             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11886
11887             cfg.cn = [
11888                 indicator,
11889                 {
11890                     tag: 'label',
11891                     'for' :  id,
11892                     cls : 'control-label',
11893                     html : this.fieldLabel
11894
11895                 },
11896                 {
11897                     cls : "", 
11898                     cn: [
11899                         combobox
11900                     ]
11901                 }
11902
11903             ];
11904             
11905             var labelCfg = cfg.cn[1];
11906             var contentCfg = cfg.cn[2];
11907             
11908             if(this.indicatorpos == 'right'){
11909                 cfg.cn = [
11910                     {
11911                         tag: 'label',
11912                         'for' :  id,
11913                         cls : 'control-label',
11914                         cn : [
11915                             {
11916                                 tag : 'span',
11917                                 html : this.fieldLabel
11918                             },
11919                             indicator
11920                         ]
11921                     },
11922                     {
11923                         cls : "", 
11924                         cn: [
11925                             combobox
11926                         ]
11927                     }
11928
11929                 ];
11930                 
11931                 labelCfg = cfg.cn[0];
11932                 contentCfg = cfg.cn[1];
11933             }
11934             
11935             if(this.labelWidth > 12){
11936                 labelCfg.style = "width: " + this.labelWidth + 'px';
11937             }
11938             
11939             if(this.labelWidth < 13 && this.labelmd == 0){
11940                 this.labelmd = this.labelWidth;
11941             }
11942             
11943             if(this.labellg > 0){
11944                 labelCfg.cls += ' col-lg-' + this.labellg;
11945                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11946             }
11947             
11948             if(this.labelmd > 0){
11949                 labelCfg.cls += ' col-md-' + this.labelmd;
11950                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11951             }
11952             
11953             if(this.labelsm > 0){
11954                 labelCfg.cls += ' col-sm-' + this.labelsm;
11955                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11956             }
11957             
11958             if(this.labelxs > 0){
11959                 labelCfg.cls += ' col-xs-' + this.labelxs;
11960                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11961             }
11962             
11963         } else if ( this.fieldLabel.length) {
11964 //                Roo.log(" label");
11965             cfg.cn = [
11966                 indicator,
11967                {
11968                    tag: 'label',
11969                    //cls : 'input-group-addon',
11970                    html : this.fieldLabel
11971
11972                },
11973
11974                combobox
11975
11976             ];
11977             
11978             if(this.indicatorpos == 'right'){
11979                 
11980                 cfg.cn = [
11981                     {
11982                        tag: 'label',
11983                        cn : [
11984                            {
11985                                tag : 'span',
11986                                html : this.fieldLabel
11987                            },
11988                            indicator
11989                        ]
11990
11991                     },
11992                     combobox
11993
11994                 ];
11995
11996             }
11997
11998         } else {
11999             
12000 //                Roo.log(" no label && no align");
12001                 cfg = combobox
12002                      
12003                 
12004         }
12005         
12006         var settings=this;
12007         ['xs','sm','md','lg'].map(function(size){
12008             if (settings[size]) {
12009                 cfg.cls += ' col-' + size + '-' + settings[size];
12010             }
12011         });
12012         
12013         return cfg;
12014         
12015     },
12016     
12017     
12018     
12019     // private
12020     onResize : function(w, h){
12021 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12022 //        if(typeof w == 'number'){
12023 //            var x = w - this.trigger.getWidth();
12024 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12025 //            this.trigger.setStyle('left', x+'px');
12026 //        }
12027     },
12028
12029     // private
12030     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12031
12032     // private
12033     getResizeEl : function(){
12034         return this.inputEl();
12035     },
12036
12037     // private
12038     getPositionEl : function(){
12039         return this.inputEl();
12040     },
12041
12042     // private
12043     alignErrorIcon : function(){
12044         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12045     },
12046
12047     // private
12048     initEvents : function(){
12049         
12050         this.createList();
12051         
12052         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12053         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12054         if(!this.multiple && this.showToggleBtn){
12055             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12056             if(this.hideTrigger){
12057                 this.trigger.setDisplayed(false);
12058             }
12059             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12060         }
12061         
12062         if(this.multiple){
12063             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12064         }
12065         
12066         if(this.removable && !this.editable && !this.tickable){
12067             var close = this.closeTriggerEl();
12068             
12069             if(close){
12070                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12071                 close.on('click', this.removeBtnClick, this, close);
12072             }
12073         }
12074         
12075         //this.trigger.addClassOnOver('x-form-trigger-over');
12076         //this.trigger.addClassOnClick('x-form-trigger-click');
12077         
12078         //if(!this.width){
12079         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12080         //}
12081     },
12082     
12083     closeTriggerEl : function()
12084     {
12085         var close = this.el.select('.roo-combo-removable-btn', true).first();
12086         return close ? close : false;
12087     },
12088     
12089     removeBtnClick : function(e, h, el)
12090     {
12091         e.preventDefault();
12092         
12093         if(this.fireEvent("remove", this) !== false){
12094             this.reset();
12095             this.fireEvent("afterremove", this)
12096         }
12097     },
12098     
12099     createList : function()
12100     {
12101         this.list = Roo.get(document.body).createChild({
12102             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12103             cls: 'typeahead typeahead-long dropdown-menu',
12104             style: 'display:none'
12105         });
12106         
12107         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12108         
12109     },
12110
12111     // private
12112     initTrigger : function(){
12113        
12114     },
12115
12116     // private
12117     onDestroy : function(){
12118         if(this.trigger){
12119             this.trigger.removeAllListeners();
12120           //  this.trigger.remove();
12121         }
12122         //if(this.wrap){
12123         //    this.wrap.remove();
12124         //}
12125         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12126     },
12127
12128     // private
12129     onFocus : function(){
12130         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12131         /*
12132         if(!this.mimicing){
12133             this.wrap.addClass('x-trigger-wrap-focus');
12134             this.mimicing = true;
12135             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12136             if(this.monitorTab){
12137                 this.el.on("keydown", this.checkTab, this);
12138             }
12139         }
12140         */
12141     },
12142
12143     // private
12144     checkTab : function(e){
12145         if(e.getKey() == e.TAB){
12146             this.triggerBlur();
12147         }
12148     },
12149
12150     // private
12151     onBlur : function(){
12152         // do nothing
12153     },
12154
12155     // private
12156     mimicBlur : function(e, t){
12157         /*
12158         if(!this.wrap.contains(t) && this.validateBlur()){
12159             this.triggerBlur();
12160         }
12161         */
12162     },
12163
12164     // private
12165     triggerBlur : function(){
12166         this.mimicing = false;
12167         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12168         if(this.monitorTab){
12169             this.el.un("keydown", this.checkTab, this);
12170         }
12171         //this.wrap.removeClass('x-trigger-wrap-focus');
12172         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12173     },
12174
12175     // private
12176     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12177     validateBlur : function(e, t){
12178         return true;
12179     },
12180
12181     // private
12182     onDisable : function(){
12183         this.inputEl().dom.disabled = true;
12184         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12185         //if(this.wrap){
12186         //    this.wrap.addClass('x-item-disabled');
12187         //}
12188     },
12189
12190     // private
12191     onEnable : function(){
12192         this.inputEl().dom.disabled = false;
12193         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12194         //if(this.wrap){
12195         //    this.el.removeClass('x-item-disabled');
12196         //}
12197     },
12198
12199     // private
12200     onShow : function(){
12201         var ae = this.getActionEl();
12202         
12203         if(ae){
12204             ae.dom.style.display = '';
12205             ae.dom.style.visibility = 'visible';
12206         }
12207     },
12208
12209     // private
12210     
12211     onHide : function(){
12212         var ae = this.getActionEl();
12213         ae.dom.style.display = 'none';
12214     },
12215
12216     /**
12217      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12218      * by an implementing function.
12219      * @method
12220      * @param {EventObject} e
12221      */
12222     onTriggerClick : Roo.emptyFn
12223 });
12224  
12225 /*
12226 * Licence: LGPL
12227 */
12228
12229 /**
12230  * @class Roo.bootstrap.CardUploader
12231  * @extends Roo.bootstrap.Button
12232  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12233  * @cfg {Number} errorTimeout default 3000
12234  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12235  * @cfg {Array}  html The button text.
12236
12237  *
12238  * @constructor
12239  * Create a new CardUploader
12240  * @param {Object} config The config object
12241  */
12242
12243 Roo.bootstrap.CardUploader = function(config){
12244     
12245  
12246     
12247     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12248     
12249     
12250     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12251         return r.data.id
12252         });
12253     
12254     
12255 };
12256
12257 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12258     
12259      
12260     errorTimeout : 3000,
12261      
12262     images : false,
12263    
12264     fileCollection : false,
12265     allowBlank : true,
12266     
12267     getAutoCreate : function()
12268     {
12269         
12270         var cfg =  {
12271             cls :'form-group' ,
12272             cn : [
12273                
12274                 {
12275                     tag: 'label',
12276                    //cls : 'input-group-addon',
12277                     html : this.fieldLabel
12278
12279                 },
12280
12281                 {
12282                     tag: 'input',
12283                     type : 'hidden',
12284                     value : this.value,
12285                     cls : 'd-none  form-control'
12286                 },
12287                 
12288                 {
12289                     tag: 'input',
12290                     multiple : 'multiple',
12291                     type : 'file',
12292                     cls : 'd-none  roo-card-upload-selector'
12293                 },
12294                 
12295                 {
12296                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12297                 },
12298                 {
12299                     cls : 'card-columns roo-card-uploader-container'
12300                 }
12301
12302             ]
12303         };
12304            
12305          
12306         return cfg;
12307     },
12308     
12309     getChildContainer : function() /// what children are added to.
12310     {
12311         return this.containerEl;
12312     },
12313    
12314     getButtonContainer : function() /// what children are added to.
12315     {
12316         return this.el.select(".roo-card-uploader-button-container").first();
12317     },
12318    
12319     initEvents : function()
12320     {
12321         
12322         Roo.bootstrap.Input.prototype.initEvents.call(this);
12323         
12324         var t = this;
12325         this.addxtype({
12326             xns: Roo.bootstrap,
12327
12328             xtype : 'Button',
12329             container_method : 'getButtonContainer' ,            
12330             html :  this.html, // fix changable?
12331             cls : 'w-100 ',
12332             listeners : {
12333                 'click' : function(btn, e) {
12334                     t.onClick(e);
12335                 }
12336             }
12337         });
12338         
12339         
12340         
12341         
12342         this.urlAPI = (window.createObjectURL && window) || 
12343                                 (window.URL && URL.revokeObjectURL && URL) || 
12344                                 (window.webkitURL && webkitURL);
12345                         
12346          
12347          
12348          
12349         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12350         
12351         this.selectorEl.on('change', this.onFileSelected, this);
12352         if (this.images) {
12353             var t = this;
12354             this.images.forEach(function(img) {
12355                 t.addCard(img)
12356             });
12357             this.images = false;
12358         }
12359         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12360          
12361        
12362     },
12363     
12364    
12365     onClick : function(e)
12366     {
12367         e.preventDefault();
12368          
12369         this.selectorEl.dom.click();
12370          
12371     },
12372     
12373     onFileSelected : function(e)
12374     {
12375         e.preventDefault();
12376         
12377         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12378             return;
12379         }
12380         
12381         Roo.each(this.selectorEl.dom.files, function(file){    
12382             this.addFile(file);
12383         }, this);
12384          
12385     },
12386     
12387       
12388     
12389       
12390     
12391     addFile : function(file)
12392     {
12393            
12394         if(typeof(file) === 'string'){
12395             throw "Add file by name?"; // should not happen
12396             return;
12397         }
12398         
12399         if(!file || !this.urlAPI){
12400             return;
12401         }
12402         
12403         // file;
12404         // file.type;
12405         
12406         var _this = this;
12407         
12408         
12409         var url = _this.urlAPI.createObjectURL( file);
12410            
12411         this.addCard({
12412             id : Roo.bootstrap.CardUploader.ID--,
12413             is_uploaded : false,
12414             src : url,
12415             title : file.name,
12416             mimetype : file.type,
12417             preview : false,
12418             is_deleted : 0
12419         })
12420         
12421     },
12422     
12423     addCard : function (data)
12424     {
12425         // hidden input element?
12426         // if the file is not an image...
12427         //then we need to use something other that and header_image
12428         var t = this;
12429         //   remove.....
12430         var footer = [
12431             {
12432                 xns : Roo.bootstrap,
12433                 xtype : 'CardFooter',
12434                 items: [
12435                     {
12436                         xns : Roo.bootstrap,
12437                         xtype : 'Element',
12438                         cls : 'd-flex',
12439                         items : [
12440                             
12441                             {
12442                                 xns : Roo.bootstrap,
12443                                 xtype : 'Button',
12444                                 html : String.format("<small>{0}</small>", data.title),
12445                                 cls : 'col-11 text-left',
12446                                 size: 'sm',
12447                                 weight: 'link',
12448                                 fa : 'download',
12449                                 listeners : {
12450                                     click : function() {
12451                                         this.downloadCard(data.id)
12452                                     }
12453                                 }
12454                             },
12455                           
12456                             {
12457                                 xns : Roo.bootstrap,
12458                                 xtype : 'Button',
12459                                 
12460                                 size : 'sm',
12461                                 weight: 'danger',
12462                                 cls : 'col-1',
12463                                 fa : 'times',
12464                                 listeners : {
12465                                     click : function() {
12466                                         t.removeCard(data.id)
12467                                     }
12468                                 }
12469                             }
12470                         ]
12471                     }
12472                     
12473                 ] 
12474             }
12475             
12476         ];
12477
12478         var cn = this.addxtype(
12479             {
12480                  
12481                 xns : Roo.bootstrap,
12482                 xtype : 'Card',
12483                 closeable : true,
12484                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12485                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12486                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12487                 data : data,
12488                 html : false,
12489                  
12490                 items : footer,
12491                 initEvents : function() {
12492                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12493                     this.imgEl = this.el.select('.card-img-top').first();
12494                     if (this.imgEl) {
12495                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12496                         this.imgEl.set({ 'pointer' : 'cursor' });
12497                                   
12498                     }
12499                     
12500                   
12501                 }
12502                 
12503             }
12504         );
12505         // dont' really need ot update items.
12506         // this.items.push(cn);
12507         this.fileCollection.add(cn);
12508         this.updateInput();
12509         
12510     },
12511     removeCard : function(id)
12512     {
12513         
12514         var card  = this.fileCollection.get(id);
12515         card.data.is_deleted = 1;
12516         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12517         this.fileCollection.remove(card);
12518         //this.items = this.items.filter(function(e) { return e != card });
12519         // dont' really need ot update items.
12520         card.el.dom.parentNode.removeChild(card.el.dom);
12521         
12522     },
12523     reset: function()
12524     {
12525         this.fileCollection.each(function(card) {
12526             card.el.dom.parentNode.removeChild(card.el.dom);    
12527         });
12528         this.fileCollection.clear();
12529         this.updateInput();
12530     },
12531     
12532     updateInput : function()
12533     {
12534         var data = [];
12535         this.fileCollection.each(function(e) {
12536             data.push(e.data);
12537         });
12538         
12539         this.inputEl().dom.value = JSON.stringify(data);
12540     }
12541     
12542     
12543 });
12544
12545
12546 Roo.bootstrap.CardUploader.ID = -1;/*
12547  * Based on:
12548  * Ext JS Library 1.1.1
12549  * Copyright(c) 2006-2007, Ext JS, LLC.
12550  *
12551  * Originally Released Under LGPL - original licence link has changed is not relivant.
12552  *
12553  * Fork - LGPL
12554  * <script type="text/javascript">
12555  */
12556
12557
12558 /**
12559  * @class Roo.data.SortTypes
12560  * @singleton
12561  * Defines the default sorting (casting?) comparison functions used when sorting data.
12562  */
12563 Roo.data.SortTypes = {
12564     /**
12565      * Default sort that does nothing
12566      * @param {Mixed} s The value being converted
12567      * @return {Mixed} The comparison value
12568      */
12569     none : function(s){
12570         return s;
12571     },
12572     
12573     /**
12574      * The regular expression used to strip tags
12575      * @type {RegExp}
12576      * @property
12577      */
12578     stripTagsRE : /<\/?[^>]+>/gi,
12579     
12580     /**
12581      * Strips all HTML tags to sort on text only
12582      * @param {Mixed} s The value being converted
12583      * @return {String} The comparison value
12584      */
12585     asText : function(s){
12586         return String(s).replace(this.stripTagsRE, "");
12587     },
12588     
12589     /**
12590      * Strips all HTML tags to sort on text only - Case insensitive
12591      * @param {Mixed} s The value being converted
12592      * @return {String} The comparison value
12593      */
12594     asUCText : function(s){
12595         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12596     },
12597     
12598     /**
12599      * Case insensitive string
12600      * @param {Mixed} s The value being converted
12601      * @return {String} The comparison value
12602      */
12603     asUCString : function(s) {
12604         return String(s).toUpperCase();
12605     },
12606     
12607     /**
12608      * Date sorting
12609      * @param {Mixed} s The value being converted
12610      * @return {Number} The comparison value
12611      */
12612     asDate : function(s) {
12613         if(!s){
12614             return 0;
12615         }
12616         if(s instanceof Date){
12617             return s.getTime();
12618         }
12619         return Date.parse(String(s));
12620     },
12621     
12622     /**
12623      * Float sorting
12624      * @param {Mixed} s The value being converted
12625      * @return {Float} The comparison value
12626      */
12627     asFloat : function(s) {
12628         var val = parseFloat(String(s).replace(/,/g, ""));
12629         if(isNaN(val)) {
12630             val = 0;
12631         }
12632         return val;
12633     },
12634     
12635     /**
12636      * Integer sorting
12637      * @param {Mixed} s The value being converted
12638      * @return {Number} The comparison value
12639      */
12640     asInt : function(s) {
12641         var val = parseInt(String(s).replace(/,/g, ""));
12642         if(isNaN(val)) {
12643             val = 0;
12644         }
12645         return val;
12646     }
12647 };/*
12648  * Based on:
12649  * Ext JS Library 1.1.1
12650  * Copyright(c) 2006-2007, Ext JS, LLC.
12651  *
12652  * Originally Released Under LGPL - original licence link has changed is not relivant.
12653  *
12654  * Fork - LGPL
12655  * <script type="text/javascript">
12656  */
12657
12658 /**
12659 * @class Roo.data.Record
12660  * Instances of this class encapsulate both record <em>definition</em> information, and record
12661  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12662  * to access Records cached in an {@link Roo.data.Store} object.<br>
12663  * <p>
12664  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12665  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12666  * objects.<br>
12667  * <p>
12668  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12669  * @constructor
12670  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12671  * {@link #create}. The parameters are the same.
12672  * @param {Array} data An associative Array of data values keyed by the field name.
12673  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12674  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12675  * not specified an integer id is generated.
12676  */
12677 Roo.data.Record = function(data, id){
12678     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12679     this.data = data;
12680 };
12681
12682 /**
12683  * Generate a constructor for a specific record layout.
12684  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12685  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12686  * Each field definition object may contain the following properties: <ul>
12687  * <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,
12688  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12689  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12690  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12691  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12692  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12693  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12694  * this may be omitted.</p></li>
12695  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12696  * <ul><li>auto (Default, implies no conversion)</li>
12697  * <li>string</li>
12698  * <li>int</li>
12699  * <li>float</li>
12700  * <li>boolean</li>
12701  * <li>date</li></ul></p></li>
12702  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12703  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12704  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12705  * by the Reader into an object that will be stored in the Record. It is passed the
12706  * following parameters:<ul>
12707  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12708  * </ul></p></li>
12709  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12710  * </ul>
12711  * <br>usage:<br><pre><code>
12712 var TopicRecord = Roo.data.Record.create(
12713     {name: 'title', mapping: 'topic_title'},
12714     {name: 'author', mapping: 'username'},
12715     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12716     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12717     {name: 'lastPoster', mapping: 'user2'},
12718     {name: 'excerpt', mapping: 'post_text'}
12719 );
12720
12721 var myNewRecord = new TopicRecord({
12722     title: 'Do my job please',
12723     author: 'noobie',
12724     totalPosts: 1,
12725     lastPost: new Date(),
12726     lastPoster: 'Animal',
12727     excerpt: 'No way dude!'
12728 });
12729 myStore.add(myNewRecord);
12730 </code></pre>
12731  * @method create
12732  * @static
12733  */
12734 Roo.data.Record.create = function(o){
12735     var f = function(){
12736         f.superclass.constructor.apply(this, arguments);
12737     };
12738     Roo.extend(f, Roo.data.Record);
12739     var p = f.prototype;
12740     p.fields = new Roo.util.MixedCollection(false, function(field){
12741         return field.name;
12742     });
12743     for(var i = 0, len = o.length; i < len; i++){
12744         p.fields.add(new Roo.data.Field(o[i]));
12745     }
12746     f.getField = function(name){
12747         return p.fields.get(name);  
12748     };
12749     return f;
12750 };
12751
12752 Roo.data.Record.AUTO_ID = 1000;
12753 Roo.data.Record.EDIT = 'edit';
12754 Roo.data.Record.REJECT = 'reject';
12755 Roo.data.Record.COMMIT = 'commit';
12756
12757 Roo.data.Record.prototype = {
12758     /**
12759      * Readonly flag - true if this record has been modified.
12760      * @type Boolean
12761      */
12762     dirty : false,
12763     editing : false,
12764     error: null,
12765     modified: null,
12766
12767     // private
12768     join : function(store){
12769         this.store = store;
12770     },
12771
12772     /**
12773      * Set the named field to the specified value.
12774      * @param {String} name The name of the field to set.
12775      * @param {Object} value The value to set the field to.
12776      */
12777     set : function(name, value){
12778         if(this.data[name] == value){
12779             return;
12780         }
12781         this.dirty = true;
12782         if(!this.modified){
12783             this.modified = {};
12784         }
12785         if(typeof this.modified[name] == 'undefined'){
12786             this.modified[name] = this.data[name];
12787         }
12788         this.data[name] = value;
12789         if(!this.editing && this.store){
12790             this.store.afterEdit(this);
12791         }       
12792     },
12793
12794     /**
12795      * Get the value of the named field.
12796      * @param {String} name The name of the field to get the value of.
12797      * @return {Object} The value of the field.
12798      */
12799     get : function(name){
12800         return this.data[name]; 
12801     },
12802
12803     // private
12804     beginEdit : function(){
12805         this.editing = true;
12806         this.modified = {}; 
12807     },
12808
12809     // private
12810     cancelEdit : function(){
12811         this.editing = false;
12812         delete this.modified;
12813     },
12814
12815     // private
12816     endEdit : function(){
12817         this.editing = false;
12818         if(this.dirty && this.store){
12819             this.store.afterEdit(this);
12820         }
12821     },
12822
12823     /**
12824      * Usually called by the {@link Roo.data.Store} which owns the Record.
12825      * Rejects all changes made to the Record since either creation, or the last commit operation.
12826      * Modified fields are reverted to their original values.
12827      * <p>
12828      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12829      * of reject operations.
12830      */
12831     reject : function(){
12832         var m = this.modified;
12833         for(var n in m){
12834             if(typeof m[n] != "function"){
12835                 this.data[n] = m[n];
12836             }
12837         }
12838         this.dirty = false;
12839         delete this.modified;
12840         this.editing = false;
12841         if(this.store){
12842             this.store.afterReject(this);
12843         }
12844     },
12845
12846     /**
12847      * Usually called by the {@link Roo.data.Store} which owns the Record.
12848      * Commits all changes made to the Record since either creation, or the last commit operation.
12849      * <p>
12850      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12851      * of commit operations.
12852      */
12853     commit : function(){
12854         this.dirty = false;
12855         delete this.modified;
12856         this.editing = false;
12857         if(this.store){
12858             this.store.afterCommit(this);
12859         }
12860     },
12861
12862     // private
12863     hasError : function(){
12864         return this.error != null;
12865     },
12866
12867     // private
12868     clearError : function(){
12869         this.error = null;
12870     },
12871
12872     /**
12873      * Creates a copy of this record.
12874      * @param {String} id (optional) A new record id if you don't want to use this record's id
12875      * @return {Record}
12876      */
12877     copy : function(newId) {
12878         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12879     }
12880 };/*
12881  * Based on:
12882  * Ext JS Library 1.1.1
12883  * Copyright(c) 2006-2007, Ext JS, LLC.
12884  *
12885  * Originally Released Under LGPL - original licence link has changed is not relivant.
12886  *
12887  * Fork - LGPL
12888  * <script type="text/javascript">
12889  */
12890
12891
12892
12893 /**
12894  * @class Roo.data.Store
12895  * @extends Roo.util.Observable
12896  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12897  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12898  * <p>
12899  * 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
12900  * has no knowledge of the format of the data returned by the Proxy.<br>
12901  * <p>
12902  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
12903  * instances from the data object. These records are cached and made available through accessor functions.
12904  * @constructor
12905  * Creates a new Store.
12906  * @param {Object} config A config object containing the objects needed for the Store to access data,
12907  * and read the data into Records.
12908  */
12909 Roo.data.Store = function(config){
12910     this.data = new Roo.util.MixedCollection(false);
12911     this.data.getKey = function(o){
12912         return o.id;
12913     };
12914     this.baseParams = {};
12915     // private
12916     this.paramNames = {
12917         "start" : "start",
12918         "limit" : "limit",
12919         "sort" : "sort",
12920         "dir" : "dir",
12921         "multisort" : "_multisort"
12922     };
12923
12924     if(config && config.data){
12925         this.inlineData = config.data;
12926         delete config.data;
12927     }
12928
12929     Roo.apply(this, config);
12930     
12931     if(this.reader){ // reader passed
12932         this.reader = Roo.factory(this.reader, Roo.data);
12933         this.reader.xmodule = this.xmodule || false;
12934         if(!this.recordType){
12935             this.recordType = this.reader.recordType;
12936         }
12937         if(this.reader.onMetaChange){
12938             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
12939         }
12940     }
12941
12942     if(this.recordType){
12943         this.fields = this.recordType.prototype.fields;
12944     }
12945     this.modified = [];
12946
12947     this.addEvents({
12948         /**
12949          * @event datachanged
12950          * Fires when the data cache has changed, and a widget which is using this Store
12951          * as a Record cache should refresh its view.
12952          * @param {Store} this
12953          */
12954         datachanged : true,
12955         /**
12956          * @event metachange
12957          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
12958          * @param {Store} this
12959          * @param {Object} meta The JSON metadata
12960          */
12961         metachange : true,
12962         /**
12963          * @event add
12964          * Fires when Records have been added to the Store
12965          * @param {Store} this
12966          * @param {Roo.data.Record[]} records The array of Records added
12967          * @param {Number} index The index at which the record(s) were added
12968          */
12969         add : true,
12970         /**
12971          * @event remove
12972          * Fires when a Record has been removed from the Store
12973          * @param {Store} this
12974          * @param {Roo.data.Record} record The Record that was removed
12975          * @param {Number} index The index at which the record was removed
12976          */
12977         remove : true,
12978         /**
12979          * @event update
12980          * Fires when a Record has been updated
12981          * @param {Store} this
12982          * @param {Roo.data.Record} record The Record that was updated
12983          * @param {String} operation The update operation being performed.  Value may be one of:
12984          * <pre><code>
12985  Roo.data.Record.EDIT
12986  Roo.data.Record.REJECT
12987  Roo.data.Record.COMMIT
12988          * </code></pre>
12989          */
12990         update : true,
12991         /**
12992          * @event clear
12993          * Fires when the data cache has been cleared.
12994          * @param {Store} this
12995          */
12996         clear : true,
12997         /**
12998          * @event beforeload
12999          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13000          * the load action will be canceled.
13001          * @param {Store} this
13002          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13003          */
13004         beforeload : true,
13005         /**
13006          * @event beforeloadadd
13007          * Fires after a new set of Records has been loaded.
13008          * @param {Store} this
13009          * @param {Roo.data.Record[]} records The Records that were loaded
13010          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13011          */
13012         beforeloadadd : true,
13013         /**
13014          * @event load
13015          * Fires after a new set of Records has been loaded, before they are added to the store.
13016          * @param {Store} this
13017          * @param {Roo.data.Record[]} records The Records that were loaded
13018          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13019          * @params {Object} return from reader
13020          */
13021         load : true,
13022         /**
13023          * @event loadexception
13024          * Fires if an exception occurs in the Proxy during loading.
13025          * Called with the signature of the Proxy's "loadexception" event.
13026          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13027          * 
13028          * @param {Proxy} 
13029          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13030          * @param {Object} load options 
13031          * @param {Object} jsonData from your request (normally this contains the Exception)
13032          */
13033         loadexception : true
13034     });
13035     
13036     if(this.proxy){
13037         this.proxy = Roo.factory(this.proxy, Roo.data);
13038         this.proxy.xmodule = this.xmodule || false;
13039         this.relayEvents(this.proxy,  ["loadexception"]);
13040     }
13041     this.sortToggle = {};
13042     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13043
13044     Roo.data.Store.superclass.constructor.call(this);
13045
13046     if(this.inlineData){
13047         this.loadData(this.inlineData);
13048         delete this.inlineData;
13049     }
13050 };
13051
13052 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13053      /**
13054     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13055     * without a remote query - used by combo/forms at present.
13056     */
13057     
13058     /**
13059     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13060     */
13061     /**
13062     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13063     */
13064     /**
13065     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13066     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13067     */
13068     /**
13069     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13070     * on any HTTP request
13071     */
13072     /**
13073     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13074     */
13075     /**
13076     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13077     */
13078     multiSort: false,
13079     /**
13080     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13081     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13082     */
13083     remoteSort : false,
13084
13085     /**
13086     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13087      * loaded or when a record is removed. (defaults to false).
13088     */
13089     pruneModifiedRecords : false,
13090
13091     // private
13092     lastOptions : null,
13093
13094     /**
13095      * Add Records to the Store and fires the add event.
13096      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13097      */
13098     add : function(records){
13099         records = [].concat(records);
13100         for(var i = 0, len = records.length; i < len; i++){
13101             records[i].join(this);
13102         }
13103         var index = this.data.length;
13104         this.data.addAll(records);
13105         this.fireEvent("add", this, records, index);
13106     },
13107
13108     /**
13109      * Remove a Record from the Store and fires the remove event.
13110      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13111      */
13112     remove : function(record){
13113         var index = this.data.indexOf(record);
13114         this.data.removeAt(index);
13115  
13116         if(this.pruneModifiedRecords){
13117             this.modified.remove(record);
13118         }
13119         this.fireEvent("remove", this, record, index);
13120     },
13121
13122     /**
13123      * Remove all Records from the Store and fires the clear event.
13124      */
13125     removeAll : function(){
13126         this.data.clear();
13127         if(this.pruneModifiedRecords){
13128             this.modified = [];
13129         }
13130         this.fireEvent("clear", this);
13131     },
13132
13133     /**
13134      * Inserts Records to the Store at the given index and fires the add event.
13135      * @param {Number} index The start index at which to insert the passed Records.
13136      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13137      */
13138     insert : function(index, records){
13139         records = [].concat(records);
13140         for(var i = 0, len = records.length; i < len; i++){
13141             this.data.insert(index, records[i]);
13142             records[i].join(this);
13143         }
13144         this.fireEvent("add", this, records, index);
13145     },
13146
13147     /**
13148      * Get the index within the cache of the passed Record.
13149      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13150      * @return {Number} The index of the passed Record. Returns -1 if not found.
13151      */
13152     indexOf : function(record){
13153         return this.data.indexOf(record);
13154     },
13155
13156     /**
13157      * Get the index within the cache of the Record with the passed id.
13158      * @param {String} id The id of the Record to find.
13159      * @return {Number} The index of the Record. Returns -1 if not found.
13160      */
13161     indexOfId : function(id){
13162         return this.data.indexOfKey(id);
13163     },
13164
13165     /**
13166      * Get the Record with the specified id.
13167      * @param {String} id The id of the Record to find.
13168      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13169      */
13170     getById : function(id){
13171         return this.data.key(id);
13172     },
13173
13174     /**
13175      * Get the Record at the specified index.
13176      * @param {Number} index The index of the Record to find.
13177      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13178      */
13179     getAt : function(index){
13180         return this.data.itemAt(index);
13181     },
13182
13183     /**
13184      * Returns a range of Records between specified indices.
13185      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13186      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13187      * @return {Roo.data.Record[]} An array of Records
13188      */
13189     getRange : function(start, end){
13190         return this.data.getRange(start, end);
13191     },
13192
13193     // private
13194     storeOptions : function(o){
13195         o = Roo.apply({}, o);
13196         delete o.callback;
13197         delete o.scope;
13198         this.lastOptions = o;
13199     },
13200
13201     /**
13202      * Loads the Record cache from the configured Proxy using the configured Reader.
13203      * <p>
13204      * If using remote paging, then the first load call must specify the <em>start</em>
13205      * and <em>limit</em> properties in the options.params property to establish the initial
13206      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13207      * <p>
13208      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13209      * and this call will return before the new data has been loaded. Perform any post-processing
13210      * in a callback function, or in a "load" event handler.</strong>
13211      * <p>
13212      * @param {Object} options An object containing properties which control loading options:<ul>
13213      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13214      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13215      * passed the following arguments:<ul>
13216      * <li>r : Roo.data.Record[]</li>
13217      * <li>options: Options object from the load call</li>
13218      * <li>success: Boolean success indicator</li></ul></li>
13219      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13220      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13221      * </ul>
13222      */
13223     load : function(options){
13224         options = options || {};
13225         if(this.fireEvent("beforeload", this, options) !== false){
13226             this.storeOptions(options);
13227             var p = Roo.apply(options.params || {}, this.baseParams);
13228             // if meta was not loaded from remote source.. try requesting it.
13229             if (!this.reader.metaFromRemote) {
13230                 p._requestMeta = 1;
13231             }
13232             if(this.sortInfo && this.remoteSort){
13233                 var pn = this.paramNames;
13234                 p[pn["sort"]] = this.sortInfo.field;
13235                 p[pn["dir"]] = this.sortInfo.direction;
13236             }
13237             if (this.multiSort) {
13238                 var pn = this.paramNames;
13239                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13240             }
13241             
13242             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13243         }
13244     },
13245
13246     /**
13247      * Reloads the Record cache from the configured Proxy using the configured Reader and
13248      * the options from the last load operation performed.
13249      * @param {Object} options (optional) An object containing properties which may override the options
13250      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13251      * the most recently used options are reused).
13252      */
13253     reload : function(options){
13254         this.load(Roo.applyIf(options||{}, this.lastOptions));
13255     },
13256
13257     // private
13258     // Called as a callback by the Reader during a load operation.
13259     loadRecords : function(o, options, success){
13260         if(!o || success === false){
13261             if(success !== false){
13262                 this.fireEvent("load", this, [], options, o);
13263             }
13264             if(options.callback){
13265                 options.callback.call(options.scope || this, [], options, false);
13266             }
13267             return;
13268         }
13269         // if data returned failure - throw an exception.
13270         if (o.success === false) {
13271             // show a message if no listener is registered.
13272             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13273                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13274             }
13275             // loadmask wil be hooked into this..
13276             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13277             return;
13278         }
13279         var r = o.records, t = o.totalRecords || r.length;
13280         
13281         this.fireEvent("beforeloadadd", this, r, options, o);
13282         
13283         if(!options || options.add !== true){
13284             if(this.pruneModifiedRecords){
13285                 this.modified = [];
13286             }
13287             for(var i = 0, len = r.length; i < len; i++){
13288                 r[i].join(this);
13289             }
13290             if(this.snapshot){
13291                 this.data = this.snapshot;
13292                 delete this.snapshot;
13293             }
13294             this.data.clear();
13295             this.data.addAll(r);
13296             this.totalLength = t;
13297             this.applySort();
13298             this.fireEvent("datachanged", this);
13299         }else{
13300             this.totalLength = Math.max(t, this.data.length+r.length);
13301             this.add(r);
13302         }
13303         
13304         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13305                 
13306             var e = new Roo.data.Record({});
13307
13308             e.set(this.parent.displayField, this.parent.emptyTitle);
13309             e.set(this.parent.valueField, '');
13310
13311             this.insert(0, e);
13312         }
13313             
13314         this.fireEvent("load", this, r, options, o);
13315         if(options.callback){
13316             options.callback.call(options.scope || this, r, options, true);
13317         }
13318     },
13319
13320
13321     /**
13322      * Loads data from a passed data block. A Reader which understands the format of the data
13323      * must have been configured in the constructor.
13324      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13325      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13326      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13327      */
13328     loadData : function(o, append){
13329         var r = this.reader.readRecords(o);
13330         this.loadRecords(r, {add: append}, true);
13331     },
13332     
13333      /**
13334      * using 'cn' the nested child reader read the child array into it's child stores.
13335      * @param {Object} rec The record with a 'children array
13336      */
13337     loadDataFromChildren : function(rec)
13338     {
13339         this.loadData(this.reader.toLoadData(rec));
13340     },
13341     
13342
13343     /**
13344      * Gets the number of cached records.
13345      * <p>
13346      * <em>If using paging, this may not be the total size of the dataset. If the data object
13347      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13348      * the data set size</em>
13349      */
13350     getCount : function(){
13351         return this.data.length || 0;
13352     },
13353
13354     /**
13355      * Gets the total number of records in the dataset as returned by the server.
13356      * <p>
13357      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13358      * the dataset size</em>
13359      */
13360     getTotalCount : function(){
13361         return this.totalLength || 0;
13362     },
13363
13364     /**
13365      * Returns the sort state of the Store as an object with two properties:
13366      * <pre><code>
13367  field {String} The name of the field by which the Records are sorted
13368  direction {String} The sort order, "ASC" or "DESC"
13369      * </code></pre>
13370      */
13371     getSortState : function(){
13372         return this.sortInfo;
13373     },
13374
13375     // private
13376     applySort : function(){
13377         if(this.sortInfo && !this.remoteSort){
13378             var s = this.sortInfo, f = s.field;
13379             var st = this.fields.get(f).sortType;
13380             var fn = function(r1, r2){
13381                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13382                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13383             };
13384             this.data.sort(s.direction, fn);
13385             if(this.snapshot && this.snapshot != this.data){
13386                 this.snapshot.sort(s.direction, fn);
13387             }
13388         }
13389     },
13390
13391     /**
13392      * Sets the default sort column and order to be used by the next load operation.
13393      * @param {String} fieldName The name of the field to sort by.
13394      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13395      */
13396     setDefaultSort : function(field, dir){
13397         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13398     },
13399
13400     /**
13401      * Sort the Records.
13402      * If remote sorting is used, the sort is performed on the server, and the cache is
13403      * reloaded. If local sorting is used, the cache is sorted internally.
13404      * @param {String} fieldName The name of the field to sort by.
13405      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13406      */
13407     sort : function(fieldName, dir){
13408         var f = this.fields.get(fieldName);
13409         if(!dir){
13410             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13411             
13412             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13413                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13414             }else{
13415                 dir = f.sortDir;
13416             }
13417         }
13418         this.sortToggle[f.name] = dir;
13419         this.sortInfo = {field: f.name, direction: dir};
13420         if(!this.remoteSort){
13421             this.applySort();
13422             this.fireEvent("datachanged", this);
13423         }else{
13424             this.load(this.lastOptions);
13425         }
13426     },
13427
13428     /**
13429      * Calls the specified function for each of the Records in the cache.
13430      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13431      * Returning <em>false</em> aborts and exits the iteration.
13432      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13433      */
13434     each : function(fn, scope){
13435         this.data.each(fn, scope);
13436     },
13437
13438     /**
13439      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13440      * (e.g., during paging).
13441      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13442      */
13443     getModifiedRecords : function(){
13444         return this.modified;
13445     },
13446
13447     // private
13448     createFilterFn : function(property, value, anyMatch){
13449         if(!value.exec){ // not a regex
13450             value = String(value);
13451             if(value.length == 0){
13452                 return false;
13453             }
13454             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13455         }
13456         return function(r){
13457             return value.test(r.data[property]);
13458         };
13459     },
13460
13461     /**
13462      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13463      * @param {String} property A field on your records
13464      * @param {Number} start The record index to start at (defaults to 0)
13465      * @param {Number} end The last record index to include (defaults to length - 1)
13466      * @return {Number} The sum
13467      */
13468     sum : function(property, start, end){
13469         var rs = this.data.items, v = 0;
13470         start = start || 0;
13471         end = (end || end === 0) ? end : rs.length-1;
13472
13473         for(var i = start; i <= end; i++){
13474             v += (rs[i].data[property] || 0);
13475         }
13476         return v;
13477     },
13478
13479     /**
13480      * Filter the records by a specified property.
13481      * @param {String} field A field on your records
13482      * @param {String/RegExp} value Either a string that the field
13483      * should start with or a RegExp to test against the field
13484      * @param {Boolean} anyMatch True to match any part not just the beginning
13485      */
13486     filter : function(property, value, anyMatch){
13487         var fn = this.createFilterFn(property, value, anyMatch);
13488         return fn ? this.filterBy(fn) : this.clearFilter();
13489     },
13490
13491     /**
13492      * Filter by a function. The specified function will be called with each
13493      * record in this data source. If the function returns true the record is included,
13494      * otherwise it is filtered.
13495      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13496      * @param {Object} scope (optional) The scope of the function (defaults to this)
13497      */
13498     filterBy : function(fn, scope){
13499         this.snapshot = this.snapshot || this.data;
13500         this.data = this.queryBy(fn, scope||this);
13501         this.fireEvent("datachanged", this);
13502     },
13503
13504     /**
13505      * Query the records by a specified property.
13506      * @param {String} field A field on your records
13507      * @param {String/RegExp} value Either a string that the field
13508      * should start with or a RegExp to test against the field
13509      * @param {Boolean} anyMatch True to match any part not just the beginning
13510      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13511      */
13512     query : function(property, value, anyMatch){
13513         var fn = this.createFilterFn(property, value, anyMatch);
13514         return fn ? this.queryBy(fn) : this.data.clone();
13515     },
13516
13517     /**
13518      * Query by a function. The specified function will be called with each
13519      * record in this data source. If the function returns true the record is included
13520      * in the results.
13521      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13522      * @param {Object} scope (optional) The scope of the function (defaults to this)
13523       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13524      **/
13525     queryBy : function(fn, scope){
13526         var data = this.snapshot || this.data;
13527         return data.filterBy(fn, scope||this);
13528     },
13529
13530     /**
13531      * Collects unique values for a particular dataIndex from this store.
13532      * @param {String} dataIndex The property to collect
13533      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13534      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13535      * @return {Array} An array of the unique values
13536      **/
13537     collect : function(dataIndex, allowNull, bypassFilter){
13538         var d = (bypassFilter === true && this.snapshot) ?
13539                 this.snapshot.items : this.data.items;
13540         var v, sv, r = [], l = {};
13541         for(var i = 0, len = d.length; i < len; i++){
13542             v = d[i].data[dataIndex];
13543             sv = String(v);
13544             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13545                 l[sv] = true;
13546                 r[r.length] = v;
13547             }
13548         }
13549         return r;
13550     },
13551
13552     /**
13553      * Revert to a view of the Record cache with no filtering applied.
13554      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13555      */
13556     clearFilter : function(suppressEvent){
13557         if(this.snapshot && this.snapshot != this.data){
13558             this.data = this.snapshot;
13559             delete this.snapshot;
13560             if(suppressEvent !== true){
13561                 this.fireEvent("datachanged", this);
13562             }
13563         }
13564     },
13565
13566     // private
13567     afterEdit : function(record){
13568         if(this.modified.indexOf(record) == -1){
13569             this.modified.push(record);
13570         }
13571         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13572     },
13573     
13574     // private
13575     afterReject : function(record){
13576         this.modified.remove(record);
13577         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13578     },
13579
13580     // private
13581     afterCommit : function(record){
13582         this.modified.remove(record);
13583         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13584     },
13585
13586     /**
13587      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13588      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13589      */
13590     commitChanges : function(){
13591         var m = this.modified.slice(0);
13592         this.modified = [];
13593         for(var i = 0, len = m.length; i < len; i++){
13594             m[i].commit();
13595         }
13596     },
13597
13598     /**
13599      * Cancel outstanding changes on all changed records.
13600      */
13601     rejectChanges : function(){
13602         var m = this.modified.slice(0);
13603         this.modified = [];
13604         for(var i = 0, len = m.length; i < len; i++){
13605             m[i].reject();
13606         }
13607     },
13608
13609     onMetaChange : function(meta, rtype, o){
13610         this.recordType = rtype;
13611         this.fields = rtype.prototype.fields;
13612         delete this.snapshot;
13613         this.sortInfo = meta.sortInfo || this.sortInfo;
13614         this.modified = [];
13615         this.fireEvent('metachange', this, this.reader.meta);
13616     },
13617     
13618     moveIndex : function(data, type)
13619     {
13620         var index = this.indexOf(data);
13621         
13622         var newIndex = index + type;
13623         
13624         this.remove(data);
13625         
13626         this.insert(newIndex, data);
13627         
13628     }
13629 });/*
13630  * Based on:
13631  * Ext JS Library 1.1.1
13632  * Copyright(c) 2006-2007, Ext JS, LLC.
13633  *
13634  * Originally Released Under LGPL - original licence link has changed is not relivant.
13635  *
13636  * Fork - LGPL
13637  * <script type="text/javascript">
13638  */
13639
13640 /**
13641  * @class Roo.data.SimpleStore
13642  * @extends Roo.data.Store
13643  * Small helper class to make creating Stores from Array data easier.
13644  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13645  * @cfg {Array} fields An array of field definition objects, or field name strings.
13646  * @cfg {Object} an existing reader (eg. copied from another store)
13647  * @cfg {Array} data The multi-dimensional array of data
13648  * @constructor
13649  * @param {Object} config
13650  */
13651 Roo.data.SimpleStore = function(config)
13652 {
13653     Roo.data.SimpleStore.superclass.constructor.call(this, {
13654         isLocal : true,
13655         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13656                 id: config.id
13657             },
13658             Roo.data.Record.create(config.fields)
13659         ),
13660         proxy : new Roo.data.MemoryProxy(config.data)
13661     });
13662     this.load();
13663 };
13664 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13665  * Based on:
13666  * Ext JS Library 1.1.1
13667  * Copyright(c) 2006-2007, Ext JS, LLC.
13668  *
13669  * Originally Released Under LGPL - original licence link has changed is not relivant.
13670  *
13671  * Fork - LGPL
13672  * <script type="text/javascript">
13673  */
13674
13675 /**
13676 /**
13677  * @extends Roo.data.Store
13678  * @class Roo.data.JsonStore
13679  * Small helper class to make creating Stores for JSON data easier. <br/>
13680 <pre><code>
13681 var store = new Roo.data.JsonStore({
13682     url: 'get-images.php',
13683     root: 'images',
13684     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13685 });
13686 </code></pre>
13687  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13688  * JsonReader and HttpProxy (unless inline data is provided).</b>
13689  * @cfg {Array} fields An array of field definition objects, or field name strings.
13690  * @constructor
13691  * @param {Object} config
13692  */
13693 Roo.data.JsonStore = function(c){
13694     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13695         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13696         reader: new Roo.data.JsonReader(c, c.fields)
13697     }));
13698 };
13699 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13700  * Based on:
13701  * Ext JS Library 1.1.1
13702  * Copyright(c) 2006-2007, Ext JS, LLC.
13703  *
13704  * Originally Released Under LGPL - original licence link has changed is not relivant.
13705  *
13706  * Fork - LGPL
13707  * <script type="text/javascript">
13708  */
13709
13710  
13711 Roo.data.Field = function(config){
13712     if(typeof config == "string"){
13713         config = {name: config};
13714     }
13715     Roo.apply(this, config);
13716     
13717     if(!this.type){
13718         this.type = "auto";
13719     }
13720     
13721     var st = Roo.data.SortTypes;
13722     // named sortTypes are supported, here we look them up
13723     if(typeof this.sortType == "string"){
13724         this.sortType = st[this.sortType];
13725     }
13726     
13727     // set default sortType for strings and dates
13728     if(!this.sortType){
13729         switch(this.type){
13730             case "string":
13731                 this.sortType = st.asUCString;
13732                 break;
13733             case "date":
13734                 this.sortType = st.asDate;
13735                 break;
13736             default:
13737                 this.sortType = st.none;
13738         }
13739     }
13740
13741     // define once
13742     var stripRe = /[\$,%]/g;
13743
13744     // prebuilt conversion function for this field, instead of
13745     // switching every time we're reading a value
13746     if(!this.convert){
13747         var cv, dateFormat = this.dateFormat;
13748         switch(this.type){
13749             case "":
13750             case "auto":
13751             case undefined:
13752                 cv = function(v){ return v; };
13753                 break;
13754             case "string":
13755                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13756                 break;
13757             case "int":
13758                 cv = function(v){
13759                     return v !== undefined && v !== null && v !== '' ?
13760                            parseInt(String(v).replace(stripRe, ""), 10) : '';
13761                     };
13762                 break;
13763             case "float":
13764                 cv = function(v){
13765                     return v !== undefined && v !== null && v !== '' ?
13766                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
13767                     };
13768                 break;
13769             case "bool":
13770             case "boolean":
13771                 cv = function(v){ return v === true || v === "true" || v == 1; };
13772                 break;
13773             case "date":
13774                 cv = function(v){
13775                     if(!v){
13776                         return '';
13777                     }
13778                     if(v instanceof Date){
13779                         return v;
13780                     }
13781                     if(dateFormat){
13782                         if(dateFormat == "timestamp"){
13783                             return new Date(v*1000);
13784                         }
13785                         return Date.parseDate(v, dateFormat);
13786                     }
13787                     var parsed = Date.parse(v);
13788                     return parsed ? new Date(parsed) : null;
13789                 };
13790              break;
13791             
13792         }
13793         this.convert = cv;
13794     }
13795 };
13796
13797 Roo.data.Field.prototype = {
13798     dateFormat: null,
13799     defaultValue: "",
13800     mapping: null,
13801     sortType : null,
13802     sortDir : "ASC"
13803 };/*
13804  * Based on:
13805  * Ext JS Library 1.1.1
13806  * Copyright(c) 2006-2007, Ext JS, LLC.
13807  *
13808  * Originally Released Under LGPL - original licence link has changed is not relivant.
13809  *
13810  * Fork - LGPL
13811  * <script type="text/javascript">
13812  */
13813  
13814 // Base class for reading structured data from a data source.  This class is intended to be
13815 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13816
13817 /**
13818  * @class Roo.data.DataReader
13819  * Base class for reading structured data from a data source.  This class is intended to be
13820  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13821  */
13822
13823 Roo.data.DataReader = function(meta, recordType){
13824     
13825     this.meta = meta;
13826     
13827     this.recordType = recordType instanceof Array ? 
13828         Roo.data.Record.create(recordType) : recordType;
13829 };
13830
13831 Roo.data.DataReader.prototype = {
13832     
13833     
13834     readerType : 'Data',
13835      /**
13836      * Create an empty record
13837      * @param {Object} data (optional) - overlay some values
13838      * @return {Roo.data.Record} record created.
13839      */
13840     newRow :  function(d) {
13841         var da =  {};
13842         this.recordType.prototype.fields.each(function(c) {
13843             switch( c.type) {
13844                 case 'int' : da[c.name] = 0; break;
13845                 case 'date' : da[c.name] = new Date(); break;
13846                 case 'float' : da[c.name] = 0.0; break;
13847                 case 'boolean' : da[c.name] = false; break;
13848                 default : da[c.name] = ""; break;
13849             }
13850             
13851         });
13852         return new this.recordType(Roo.apply(da, d));
13853     }
13854     
13855     
13856 };/*
13857  * Based on:
13858  * Ext JS Library 1.1.1
13859  * Copyright(c) 2006-2007, Ext JS, LLC.
13860  *
13861  * Originally Released Under LGPL - original licence link has changed is not relivant.
13862  *
13863  * Fork - LGPL
13864  * <script type="text/javascript">
13865  */
13866
13867 /**
13868  * @class Roo.data.DataProxy
13869  * @extends Roo.data.Observable
13870  * This class is an abstract base class for implementations which provide retrieval of
13871  * unformatted data objects.<br>
13872  * <p>
13873  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13874  * (of the appropriate type which knows how to parse the data object) to provide a block of
13875  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13876  * <p>
13877  * Custom implementations must implement the load method as described in
13878  * {@link Roo.data.HttpProxy#load}.
13879  */
13880 Roo.data.DataProxy = function(){
13881     this.addEvents({
13882         /**
13883          * @event beforeload
13884          * Fires before a network request is made to retrieve a data object.
13885          * @param {Object} This DataProxy object.
13886          * @param {Object} params The params parameter to the load function.
13887          */
13888         beforeload : true,
13889         /**
13890          * @event load
13891          * Fires before the load method's callback is called.
13892          * @param {Object} This DataProxy object.
13893          * @param {Object} o The data object.
13894          * @param {Object} arg The callback argument object passed to the load function.
13895          */
13896         load : true,
13897         /**
13898          * @event loadexception
13899          * Fires if an Exception occurs during data retrieval.
13900          * @param {Object} This DataProxy object.
13901          * @param {Object} o The data object.
13902          * @param {Object} arg The callback argument object passed to the load function.
13903          * @param {Object} e The Exception.
13904          */
13905         loadexception : true
13906     });
13907     Roo.data.DataProxy.superclass.constructor.call(this);
13908 };
13909
13910 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
13911
13912     /**
13913      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
13914      */
13915 /*
13916  * Based on:
13917  * Ext JS Library 1.1.1
13918  * Copyright(c) 2006-2007, Ext JS, LLC.
13919  *
13920  * Originally Released Under LGPL - original licence link has changed is not relivant.
13921  *
13922  * Fork - LGPL
13923  * <script type="text/javascript">
13924  */
13925 /**
13926  * @class Roo.data.MemoryProxy
13927  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
13928  * to the Reader when its load method is called.
13929  * @constructor
13930  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
13931  */
13932 Roo.data.MemoryProxy = function(data){
13933     if (data.data) {
13934         data = data.data;
13935     }
13936     Roo.data.MemoryProxy.superclass.constructor.call(this);
13937     this.data = data;
13938 };
13939
13940 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
13941     
13942     /**
13943      * Load data from the requested source (in this case an in-memory
13944      * data object passed to the constructor), read the data object into
13945      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13946      * process that block using the passed callback.
13947      * @param {Object} params This parameter is not used by the MemoryProxy class.
13948      * @param {Roo.data.DataReader} reader The Reader object which converts the data
13949      * object into a block of Roo.data.Records.
13950      * @param {Function} callback The function into which to pass the block of Roo.data.records.
13951      * The function must be passed <ul>
13952      * <li>The Record block object</li>
13953      * <li>The "arg" argument from the load function</li>
13954      * <li>A boolean success indicator</li>
13955      * </ul>
13956      * @param {Object} scope The scope in which to call the callback
13957      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13958      */
13959     load : function(params, reader, callback, scope, arg){
13960         params = params || {};
13961         var result;
13962         try {
13963             result = reader.readRecords(params.data ? params.data :this.data);
13964         }catch(e){
13965             this.fireEvent("loadexception", this, arg, null, e);
13966             callback.call(scope, null, arg, false);
13967             return;
13968         }
13969         callback.call(scope, result, arg, true);
13970     },
13971     
13972     // private
13973     update : function(params, records){
13974         
13975     }
13976 });/*
13977  * Based on:
13978  * Ext JS Library 1.1.1
13979  * Copyright(c) 2006-2007, Ext JS, LLC.
13980  *
13981  * Originally Released Under LGPL - original licence link has changed is not relivant.
13982  *
13983  * Fork - LGPL
13984  * <script type="text/javascript">
13985  */
13986 /**
13987  * @class Roo.data.HttpProxy
13988  * @extends Roo.data.DataProxy
13989  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
13990  * configured to reference a certain URL.<br><br>
13991  * <p>
13992  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
13993  * from which the running page was served.<br><br>
13994  * <p>
13995  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
13996  * <p>
13997  * Be aware that to enable the browser to parse an XML document, the server must set
13998  * the Content-Type header in the HTTP response to "text/xml".
13999  * @constructor
14000  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14001  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14002  * will be used to make the request.
14003  */
14004 Roo.data.HttpProxy = function(conn){
14005     Roo.data.HttpProxy.superclass.constructor.call(this);
14006     // is conn a conn config or a real conn?
14007     this.conn = conn;
14008     this.useAjax = !conn || !conn.events;
14009   
14010 };
14011
14012 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14013     // thse are take from connection...
14014     
14015     /**
14016      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14017      */
14018     /**
14019      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14020      * extra parameters to each request made by this object. (defaults to undefined)
14021      */
14022     /**
14023      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14024      *  to each request made by this object. (defaults to undefined)
14025      */
14026     /**
14027      * @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)
14028      */
14029     /**
14030      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14031      */
14032      /**
14033      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14034      * @type Boolean
14035      */
14036   
14037
14038     /**
14039      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14040      * @type Boolean
14041      */
14042     /**
14043      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14044      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14045      * a finer-grained basis than the DataProxy events.
14046      */
14047     getConnection : function(){
14048         return this.useAjax ? Roo.Ajax : this.conn;
14049     },
14050
14051     /**
14052      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14053      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14054      * process that block using the passed callback.
14055      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14056      * for the request to the remote server.
14057      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14058      * object into a block of Roo.data.Records.
14059      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14060      * The function must be passed <ul>
14061      * <li>The Record block object</li>
14062      * <li>The "arg" argument from the load function</li>
14063      * <li>A boolean success indicator</li>
14064      * </ul>
14065      * @param {Object} scope The scope in which to call the callback
14066      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14067      */
14068     load : function(params, reader, callback, scope, arg){
14069         if(this.fireEvent("beforeload", this, params) !== false){
14070             var  o = {
14071                 params : params || {},
14072                 request: {
14073                     callback : callback,
14074                     scope : scope,
14075                     arg : arg
14076                 },
14077                 reader: reader,
14078                 callback : this.loadResponse,
14079                 scope: this
14080             };
14081             if(this.useAjax){
14082                 Roo.applyIf(o, this.conn);
14083                 if(this.activeRequest){
14084                     Roo.Ajax.abort(this.activeRequest);
14085                 }
14086                 this.activeRequest = Roo.Ajax.request(o);
14087             }else{
14088                 this.conn.request(o);
14089             }
14090         }else{
14091             callback.call(scope||this, null, arg, false);
14092         }
14093     },
14094
14095     // private
14096     loadResponse : function(o, success, response){
14097         delete this.activeRequest;
14098         if(!success){
14099             this.fireEvent("loadexception", this, o, response);
14100             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14101             return;
14102         }
14103         var result;
14104         try {
14105             result = o.reader.read(response);
14106         }catch(e){
14107             this.fireEvent("loadexception", this, o, response, e);
14108             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14109             return;
14110         }
14111         
14112         this.fireEvent("load", this, o, o.request.arg);
14113         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14114     },
14115
14116     // private
14117     update : function(dataSet){
14118
14119     },
14120
14121     // private
14122     updateResponse : function(dataSet){
14123
14124     }
14125 });/*
14126  * Based on:
14127  * Ext JS Library 1.1.1
14128  * Copyright(c) 2006-2007, Ext JS, LLC.
14129  *
14130  * Originally Released Under LGPL - original licence link has changed is not relivant.
14131  *
14132  * Fork - LGPL
14133  * <script type="text/javascript">
14134  */
14135
14136 /**
14137  * @class Roo.data.ScriptTagProxy
14138  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14139  * other than the originating domain of the running page.<br><br>
14140  * <p>
14141  * <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
14142  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14143  * <p>
14144  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14145  * source code that is used as the source inside a &lt;script> tag.<br><br>
14146  * <p>
14147  * In order for the browser to process the returned data, the server must wrap the data object
14148  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14149  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14150  * depending on whether the callback name was passed:
14151  * <p>
14152  * <pre><code>
14153 boolean scriptTag = false;
14154 String cb = request.getParameter("callback");
14155 if (cb != null) {
14156     scriptTag = true;
14157     response.setContentType("text/javascript");
14158 } else {
14159     response.setContentType("application/x-json");
14160 }
14161 Writer out = response.getWriter();
14162 if (scriptTag) {
14163     out.write(cb + "(");
14164 }
14165 out.print(dataBlock.toJsonString());
14166 if (scriptTag) {
14167     out.write(");");
14168 }
14169 </pre></code>
14170  *
14171  * @constructor
14172  * @param {Object} config A configuration object.
14173  */
14174 Roo.data.ScriptTagProxy = function(config){
14175     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14176     Roo.apply(this, config);
14177     this.head = document.getElementsByTagName("head")[0];
14178 };
14179
14180 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14181
14182 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14183     /**
14184      * @cfg {String} url The URL from which to request the data object.
14185      */
14186     /**
14187      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14188      */
14189     timeout : 30000,
14190     /**
14191      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14192      * the server the name of the callback function set up by the load call to process the returned data object.
14193      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14194      * javascript output which calls this named function passing the data object as its only parameter.
14195      */
14196     callbackParam : "callback",
14197     /**
14198      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14199      * name to the request.
14200      */
14201     nocache : true,
14202
14203     /**
14204      * Load data from the configured URL, read the data object into
14205      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14206      * process that block using the passed callback.
14207      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14208      * for the request to the remote server.
14209      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14210      * object into a block of Roo.data.Records.
14211      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14212      * The function must be passed <ul>
14213      * <li>The Record block object</li>
14214      * <li>The "arg" argument from the load function</li>
14215      * <li>A boolean success indicator</li>
14216      * </ul>
14217      * @param {Object} scope The scope in which to call the callback
14218      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14219      */
14220     load : function(params, reader, callback, scope, arg){
14221         if(this.fireEvent("beforeload", this, params) !== false){
14222
14223             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14224
14225             var url = this.url;
14226             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14227             if(this.nocache){
14228                 url += "&_dc=" + (new Date().getTime());
14229             }
14230             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14231             var trans = {
14232                 id : transId,
14233                 cb : "stcCallback"+transId,
14234                 scriptId : "stcScript"+transId,
14235                 params : params,
14236                 arg : arg,
14237                 url : url,
14238                 callback : callback,
14239                 scope : scope,
14240                 reader : reader
14241             };
14242             var conn = this;
14243
14244             window[trans.cb] = function(o){
14245                 conn.handleResponse(o, trans);
14246             };
14247
14248             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14249
14250             if(this.autoAbort !== false){
14251                 this.abort();
14252             }
14253
14254             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14255
14256             var script = document.createElement("script");
14257             script.setAttribute("src", url);
14258             script.setAttribute("type", "text/javascript");
14259             script.setAttribute("id", trans.scriptId);
14260             this.head.appendChild(script);
14261
14262             this.trans = trans;
14263         }else{
14264             callback.call(scope||this, null, arg, false);
14265         }
14266     },
14267
14268     // private
14269     isLoading : function(){
14270         return this.trans ? true : false;
14271     },
14272
14273     /**
14274      * Abort the current server request.
14275      */
14276     abort : function(){
14277         if(this.isLoading()){
14278             this.destroyTrans(this.trans);
14279         }
14280     },
14281
14282     // private
14283     destroyTrans : function(trans, isLoaded){
14284         this.head.removeChild(document.getElementById(trans.scriptId));
14285         clearTimeout(trans.timeoutId);
14286         if(isLoaded){
14287             window[trans.cb] = undefined;
14288             try{
14289                 delete window[trans.cb];
14290             }catch(e){}
14291         }else{
14292             // if hasn't been loaded, wait for load to remove it to prevent script error
14293             window[trans.cb] = function(){
14294                 window[trans.cb] = undefined;
14295                 try{
14296                     delete window[trans.cb];
14297                 }catch(e){}
14298             };
14299         }
14300     },
14301
14302     // private
14303     handleResponse : function(o, trans){
14304         this.trans = false;
14305         this.destroyTrans(trans, true);
14306         var result;
14307         try {
14308             result = trans.reader.readRecords(o);
14309         }catch(e){
14310             this.fireEvent("loadexception", this, o, trans.arg, e);
14311             trans.callback.call(trans.scope||window, null, trans.arg, false);
14312             return;
14313         }
14314         this.fireEvent("load", this, o, trans.arg);
14315         trans.callback.call(trans.scope||window, result, trans.arg, true);
14316     },
14317
14318     // private
14319     handleFailure : function(trans){
14320         this.trans = false;
14321         this.destroyTrans(trans, false);
14322         this.fireEvent("loadexception", this, null, trans.arg);
14323         trans.callback.call(trans.scope||window, null, trans.arg, false);
14324     }
14325 });/*
14326  * Based on:
14327  * Ext JS Library 1.1.1
14328  * Copyright(c) 2006-2007, Ext JS, LLC.
14329  *
14330  * Originally Released Under LGPL - original licence link has changed is not relivant.
14331  *
14332  * Fork - LGPL
14333  * <script type="text/javascript">
14334  */
14335
14336 /**
14337  * @class Roo.data.JsonReader
14338  * @extends Roo.data.DataReader
14339  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14340  * based on mappings in a provided Roo.data.Record constructor.
14341  * 
14342  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14343  * in the reply previously. 
14344  * 
14345  * <p>
14346  * Example code:
14347  * <pre><code>
14348 var RecordDef = Roo.data.Record.create([
14349     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14350     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14351 ]);
14352 var myReader = new Roo.data.JsonReader({
14353     totalProperty: "results",    // The property which contains the total dataset size (optional)
14354     root: "rows",                // The property which contains an Array of row objects
14355     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14356 }, RecordDef);
14357 </code></pre>
14358  * <p>
14359  * This would consume a JSON file like this:
14360  * <pre><code>
14361 { 'results': 2, 'rows': [
14362     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14363     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14364 }
14365 </code></pre>
14366  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14367  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14368  * paged from the remote server.
14369  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14370  * @cfg {String} root name of the property which contains the Array of row objects.
14371  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14372  * @cfg {Array} fields Array of field definition objects
14373  * @constructor
14374  * Create a new JsonReader
14375  * @param {Object} meta Metadata configuration options
14376  * @param {Object} recordType Either an Array of field definition objects,
14377  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14378  */
14379 Roo.data.JsonReader = function(meta, recordType){
14380     
14381     meta = meta || {};
14382     // set some defaults:
14383     Roo.applyIf(meta, {
14384         totalProperty: 'total',
14385         successProperty : 'success',
14386         root : 'data',
14387         id : 'id'
14388     });
14389     
14390     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14391 };
14392 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14393     
14394     readerType : 'Json',
14395     
14396     /**
14397      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14398      * Used by Store query builder to append _requestMeta to params.
14399      * 
14400      */
14401     metaFromRemote : false,
14402     /**
14403      * This method is only used by a DataProxy which has retrieved data from a remote server.
14404      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14405      * @return {Object} data A data block which is used by an Roo.data.Store object as
14406      * a cache of Roo.data.Records.
14407      */
14408     read : function(response){
14409         var json = response.responseText;
14410        
14411         var o = /* eval:var:o */ eval("("+json+")");
14412         if(!o) {
14413             throw {message: "JsonReader.read: Json object not found"};
14414         }
14415         
14416         if(o.metaData){
14417             
14418             delete this.ef;
14419             this.metaFromRemote = true;
14420             this.meta = o.metaData;
14421             this.recordType = Roo.data.Record.create(o.metaData.fields);
14422             this.onMetaChange(this.meta, this.recordType, o);
14423         }
14424         return this.readRecords(o);
14425     },
14426
14427     // private function a store will implement
14428     onMetaChange : function(meta, recordType, o){
14429
14430     },
14431
14432     /**
14433          * @ignore
14434          */
14435     simpleAccess: function(obj, subsc) {
14436         return obj[subsc];
14437     },
14438
14439         /**
14440          * @ignore
14441          */
14442     getJsonAccessor: function(){
14443         var re = /[\[\.]/;
14444         return function(expr) {
14445             try {
14446                 return(re.test(expr))
14447                     ? new Function("obj", "return obj." + expr)
14448                     : function(obj){
14449                         return obj[expr];
14450                     };
14451             } catch(e){}
14452             return Roo.emptyFn;
14453         };
14454     }(),
14455
14456     /**
14457      * Create a data block containing Roo.data.Records from an XML document.
14458      * @param {Object} o An object which contains an Array of row objects in the property specified
14459      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14460      * which contains the total size of the dataset.
14461      * @return {Object} data A data block which is used by an Roo.data.Store object as
14462      * a cache of Roo.data.Records.
14463      */
14464     readRecords : function(o){
14465         /**
14466          * After any data loads, the raw JSON data is available for further custom processing.
14467          * @type Object
14468          */
14469         this.o = o;
14470         var s = this.meta, Record = this.recordType,
14471             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14472
14473 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14474         if (!this.ef) {
14475             if(s.totalProperty) {
14476                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14477                 }
14478                 if(s.successProperty) {
14479                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14480                 }
14481                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14482                 if (s.id) {
14483                         var g = this.getJsonAccessor(s.id);
14484                         this.getId = function(rec) {
14485                                 var r = g(rec);  
14486                                 return (r === undefined || r === "") ? null : r;
14487                         };
14488                 } else {
14489                         this.getId = function(){return null;};
14490                 }
14491             this.ef = [];
14492             for(var jj = 0; jj < fl; jj++){
14493                 f = fi[jj];
14494                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14495                 this.ef[jj] = this.getJsonAccessor(map);
14496             }
14497         }
14498
14499         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14500         if(s.totalProperty){
14501             var vt = parseInt(this.getTotal(o), 10);
14502             if(!isNaN(vt)){
14503                 totalRecords = vt;
14504             }
14505         }
14506         if(s.successProperty){
14507             var vs = this.getSuccess(o);
14508             if(vs === false || vs === 'false'){
14509                 success = false;
14510             }
14511         }
14512         var records = [];
14513         for(var i = 0; i < c; i++){
14514                 var n = root[i];
14515             var values = {};
14516             var id = this.getId(n);
14517             for(var j = 0; j < fl; j++){
14518                 f = fi[j];
14519             var v = this.ef[j](n);
14520             if (!f.convert) {
14521                 Roo.log('missing convert for ' + f.name);
14522                 Roo.log(f);
14523                 continue;
14524             }
14525             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14526             }
14527             var record = new Record(values, id);
14528             record.json = n;
14529             records[i] = record;
14530         }
14531         return {
14532             raw : o,
14533             success : success,
14534             records : records,
14535             totalRecords : totalRecords
14536         };
14537     },
14538     // used when loading children.. @see loadDataFromChildren
14539     toLoadData: function(rec)
14540     {
14541         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14542         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14543         return { data : data, total : data.length };
14544         
14545     }
14546 });/*
14547  * Based on:
14548  * Ext JS Library 1.1.1
14549  * Copyright(c) 2006-2007, Ext JS, LLC.
14550  *
14551  * Originally Released Under LGPL - original licence link has changed is not relivant.
14552  *
14553  * Fork - LGPL
14554  * <script type="text/javascript">
14555  */
14556
14557 /**
14558  * @class Roo.data.ArrayReader
14559  * @extends Roo.data.DataReader
14560  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14561  * Each element of that Array represents a row of data fields. The
14562  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14563  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14564  * <p>
14565  * Example code:.
14566  * <pre><code>
14567 var RecordDef = Roo.data.Record.create([
14568     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14569     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14570 ]);
14571 var myReader = new Roo.data.ArrayReader({
14572     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14573 }, RecordDef);
14574 </code></pre>
14575  * <p>
14576  * This would consume an Array like this:
14577  * <pre><code>
14578 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14579   </code></pre>
14580  
14581  * @constructor
14582  * Create a new JsonReader
14583  * @param {Object} meta Metadata configuration options.
14584  * @param {Object|Array} recordType Either an Array of field definition objects
14585  * 
14586  * @cfg {Array} fields Array of field definition objects
14587  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14588  * as specified to {@link Roo.data.Record#create},
14589  * or an {@link Roo.data.Record} object
14590  *
14591  * 
14592  * created using {@link Roo.data.Record#create}.
14593  */
14594 Roo.data.ArrayReader = function(meta, recordType)
14595 {    
14596     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14597 };
14598
14599 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14600     
14601       /**
14602      * Create a data block containing Roo.data.Records from an XML document.
14603      * @param {Object} o An Array of row objects which represents the dataset.
14604      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14605      * a cache of Roo.data.Records.
14606      */
14607     readRecords : function(o)
14608     {
14609         var sid = this.meta ? this.meta.id : null;
14610         var recordType = this.recordType, fields = recordType.prototype.fields;
14611         var records = [];
14612         var root = o;
14613         for(var i = 0; i < root.length; i++){
14614                 var n = root[i];
14615             var values = {};
14616             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14617             for(var j = 0, jlen = fields.length; j < jlen; j++){
14618                 var f = fields.items[j];
14619                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14620                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14621                 v = f.convert(v);
14622                 values[f.name] = v;
14623             }
14624             var record = new recordType(values, id);
14625             record.json = n;
14626             records[records.length] = record;
14627         }
14628         return {
14629             records : records,
14630             totalRecords : records.length
14631         };
14632     },
14633     // used when loading children.. @see loadDataFromChildren
14634     toLoadData: function(rec)
14635     {
14636         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14637         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14638         
14639     }
14640     
14641     
14642 });/*
14643  * - LGPL
14644  * * 
14645  */
14646
14647 /**
14648  * @class Roo.bootstrap.ComboBox
14649  * @extends Roo.bootstrap.TriggerField
14650  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14651  * @cfg {Boolean} append (true|false) default false
14652  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14653  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14654  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14655  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14656  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14657  * @cfg {Boolean} animate default true
14658  * @cfg {Boolean} emptyResultText only for touch device
14659  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14660  * @cfg {String} emptyTitle default ''
14661  * @constructor
14662  * Create a new ComboBox.
14663  * @param {Object} config Configuration options
14664  */
14665 Roo.bootstrap.ComboBox = function(config){
14666     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14667     this.addEvents({
14668         /**
14669          * @event expand
14670          * Fires when the dropdown list is expanded
14671         * @param {Roo.bootstrap.ComboBox} combo This combo box
14672         */
14673         'expand' : true,
14674         /**
14675          * @event collapse
14676          * Fires when the dropdown list is collapsed
14677         * @param {Roo.bootstrap.ComboBox} combo This combo box
14678         */
14679         'collapse' : true,
14680         /**
14681          * @event beforeselect
14682          * Fires before a list item is selected. Return false to cancel the selection.
14683         * @param {Roo.bootstrap.ComboBox} combo This combo box
14684         * @param {Roo.data.Record} record The data record returned from the underlying store
14685         * @param {Number} index The index of the selected item in the dropdown list
14686         */
14687         'beforeselect' : true,
14688         /**
14689          * @event select
14690          * Fires when a list item is selected
14691         * @param {Roo.bootstrap.ComboBox} combo This combo box
14692         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14693         * @param {Number} index The index of the selected item in the dropdown list
14694         */
14695         'select' : true,
14696         /**
14697          * @event beforequery
14698          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14699          * The event object passed has these properties:
14700         * @param {Roo.bootstrap.ComboBox} combo This combo box
14701         * @param {String} query The query
14702         * @param {Boolean} forceAll true to force "all" query
14703         * @param {Boolean} cancel true to cancel the query
14704         * @param {Object} e The query event object
14705         */
14706         'beforequery': true,
14707          /**
14708          * @event add
14709          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14710         * @param {Roo.bootstrap.ComboBox} combo This combo box
14711         */
14712         'add' : true,
14713         /**
14714          * @event edit
14715          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14716         * @param {Roo.bootstrap.ComboBox} combo This combo box
14717         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14718         */
14719         'edit' : true,
14720         /**
14721          * @event remove
14722          * Fires when the remove value from the combobox array
14723         * @param {Roo.bootstrap.ComboBox} combo This combo box
14724         */
14725         'remove' : true,
14726         /**
14727          * @event afterremove
14728          * Fires when the remove value from the combobox array
14729         * @param {Roo.bootstrap.ComboBox} combo This combo box
14730         */
14731         'afterremove' : true,
14732         /**
14733          * @event specialfilter
14734          * Fires when specialfilter
14735             * @param {Roo.bootstrap.ComboBox} combo This combo box
14736             */
14737         'specialfilter' : true,
14738         /**
14739          * @event tick
14740          * Fires when tick the element
14741             * @param {Roo.bootstrap.ComboBox} combo This combo box
14742             */
14743         'tick' : true,
14744         /**
14745          * @event touchviewdisplay
14746          * Fires when touch view require special display (default is using displayField)
14747             * @param {Roo.bootstrap.ComboBox} combo This combo box
14748             * @param {Object} cfg set html .
14749             */
14750         'touchviewdisplay' : true
14751         
14752     });
14753     
14754     this.item = [];
14755     this.tickItems = [];
14756     
14757     this.selectedIndex = -1;
14758     if(this.mode == 'local'){
14759         if(config.queryDelay === undefined){
14760             this.queryDelay = 10;
14761         }
14762         if(config.minChars === undefined){
14763             this.minChars = 0;
14764         }
14765     }
14766 };
14767
14768 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14769      
14770     /**
14771      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14772      * rendering into an Roo.Editor, defaults to false)
14773      */
14774     /**
14775      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14776      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14777      */
14778     /**
14779      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14780      */
14781     /**
14782      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14783      * the dropdown list (defaults to undefined, with no header element)
14784      */
14785
14786      /**
14787      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
14788      */
14789      
14790      /**
14791      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14792      */
14793     listWidth: undefined,
14794     /**
14795      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14796      * mode = 'remote' or 'text' if mode = 'local')
14797      */
14798     displayField: undefined,
14799     
14800     /**
14801      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14802      * mode = 'remote' or 'value' if mode = 'local'). 
14803      * Note: use of a valueField requires the user make a selection
14804      * in order for a value to be mapped.
14805      */
14806     valueField: undefined,
14807     /**
14808      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14809      */
14810     modalTitle : '',
14811     
14812     /**
14813      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14814      * field's data value (defaults to the underlying DOM element's name)
14815      */
14816     hiddenName: undefined,
14817     /**
14818      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14819      */
14820     listClass: '',
14821     /**
14822      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14823      */
14824     selectedClass: 'active',
14825     
14826     /**
14827      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14828      */
14829     shadow:'sides',
14830     /**
14831      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14832      * anchor positions (defaults to 'tl-bl')
14833      */
14834     listAlign: 'tl-bl?',
14835     /**
14836      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14837      */
14838     maxHeight: 300,
14839     /**
14840      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
14841      * query specified by the allQuery config option (defaults to 'query')
14842      */
14843     triggerAction: 'query',
14844     /**
14845      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14846      * (defaults to 4, does not apply if editable = false)
14847      */
14848     minChars : 4,
14849     /**
14850      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14851      * delay (typeAheadDelay) if it matches a known value (defaults to false)
14852      */
14853     typeAhead: false,
14854     /**
14855      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14856      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14857      */
14858     queryDelay: 500,
14859     /**
14860      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14861      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
14862      */
14863     pageSize: 0,
14864     /**
14865      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
14866      * when editable = true (defaults to false)
14867      */
14868     selectOnFocus:false,
14869     /**
14870      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14871      */
14872     queryParam: 'query',
14873     /**
14874      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
14875      * when mode = 'remote' (defaults to 'Loading...')
14876      */
14877     loadingText: 'Loading...',
14878     /**
14879      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14880      */
14881     resizable: false,
14882     /**
14883      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14884      */
14885     handleHeight : 8,
14886     /**
14887      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14888      * traditional select (defaults to true)
14889      */
14890     editable: true,
14891     /**
14892      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14893      */
14894     allQuery: '',
14895     /**
14896      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
14897      */
14898     mode: 'remote',
14899     /**
14900      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
14901      * listWidth has a higher value)
14902      */
14903     minListWidth : 70,
14904     /**
14905      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
14906      * allow the user to set arbitrary text into the field (defaults to false)
14907      */
14908     forceSelection:false,
14909     /**
14910      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
14911      * if typeAhead = true (defaults to 250)
14912      */
14913     typeAheadDelay : 250,
14914     /**
14915      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
14916      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
14917      */
14918     valueNotFoundText : undefined,
14919     /**
14920      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
14921      */
14922     blockFocus : false,
14923     
14924     /**
14925      * @cfg {Boolean} disableClear Disable showing of clear button.
14926      */
14927     disableClear : false,
14928     /**
14929      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
14930      */
14931     alwaysQuery : false,
14932     
14933     /**
14934      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
14935      */
14936     multiple : false,
14937     
14938     /**
14939      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
14940      */
14941     invalidClass : "has-warning",
14942     
14943     /**
14944      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
14945      */
14946     validClass : "has-success",
14947     
14948     /**
14949      * @cfg {Boolean} specialFilter (true|false) special filter default false
14950      */
14951     specialFilter : false,
14952     
14953     /**
14954      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
14955      */
14956     mobileTouchView : true,
14957     
14958     /**
14959      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
14960      */
14961     useNativeIOS : false,
14962     
14963     /**
14964      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
14965      */
14966     mobile_restrict_height : false,
14967     
14968     ios_options : false,
14969     
14970     //private
14971     addicon : false,
14972     editicon: false,
14973     
14974     page: 0,
14975     hasQuery: false,
14976     append: false,
14977     loadNext: false,
14978     autoFocus : true,
14979     tickable : false,
14980     btnPosition : 'right',
14981     triggerList : true,
14982     showToggleBtn : true,
14983     animate : true,
14984     emptyResultText: 'Empty',
14985     triggerText : 'Select',
14986     emptyTitle : '',
14987     
14988     // element that contains real text value.. (when hidden is used..)
14989     
14990     getAutoCreate : function()
14991     {   
14992         var cfg = false;
14993         //render
14994         /*
14995          * Render classic select for iso
14996          */
14997         
14998         if(Roo.isIOS && this.useNativeIOS){
14999             cfg = this.getAutoCreateNativeIOS();
15000             return cfg;
15001         }
15002         
15003         /*
15004          * Touch Devices
15005          */
15006         
15007         if(Roo.isTouch && this.mobileTouchView){
15008             cfg = this.getAutoCreateTouchView();
15009             return cfg;;
15010         }
15011         
15012         /*
15013          *  Normal ComboBox
15014          */
15015         if(!this.tickable){
15016             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15017             return cfg;
15018         }
15019         
15020         /*
15021          *  ComboBox with tickable selections
15022          */
15023              
15024         var align = this.labelAlign || this.parentLabelAlign();
15025         
15026         cfg = {
15027             cls : 'form-group roo-combobox-tickable' //input-group
15028         };
15029         
15030         var btn_text_select = '';
15031         var btn_text_done = '';
15032         var btn_text_cancel = '';
15033         
15034         if (this.btn_text_show) {
15035             btn_text_select = 'Select';
15036             btn_text_done = 'Done';
15037             btn_text_cancel = 'Cancel'; 
15038         }
15039         
15040         var buttons = {
15041             tag : 'div',
15042             cls : 'tickable-buttons',
15043             cn : [
15044                 {
15045                     tag : 'button',
15046                     type : 'button',
15047                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15048                     //html : this.triggerText
15049                     html: btn_text_select
15050                 },
15051                 {
15052                     tag : 'button',
15053                     type : 'button',
15054                     name : 'ok',
15055                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15056                     //html : 'Done'
15057                     html: btn_text_done
15058                 },
15059                 {
15060                     tag : 'button',
15061                     type : 'button',
15062                     name : 'cancel',
15063                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15064                     //html : 'Cancel'
15065                     html: btn_text_cancel
15066                 }
15067             ]
15068         };
15069         
15070         if(this.editable){
15071             buttons.cn.unshift({
15072                 tag: 'input',
15073                 cls: 'roo-select2-search-field-input'
15074             });
15075         }
15076         
15077         var _this = this;
15078         
15079         Roo.each(buttons.cn, function(c){
15080             if (_this.size) {
15081                 c.cls += ' btn-' + _this.size;
15082             }
15083
15084             if (_this.disabled) {
15085                 c.disabled = true;
15086             }
15087         });
15088         
15089         var box = {
15090             tag: 'div',
15091             style : 'display: contents',
15092             cn: [
15093                 {
15094                     tag: 'input',
15095                     type : 'hidden',
15096                     cls: 'form-hidden-field'
15097                 },
15098                 {
15099                     tag: 'ul',
15100                     cls: 'roo-select2-choices',
15101                     cn:[
15102                         {
15103                             tag: 'li',
15104                             cls: 'roo-select2-search-field',
15105                             cn: [
15106                                 buttons
15107                             ]
15108                         }
15109                     ]
15110                 }
15111             ]
15112         };
15113         
15114         var combobox = {
15115             cls: 'roo-select2-container input-group roo-select2-container-multi',
15116             cn: [
15117                 
15118                 box
15119 //                {
15120 //                    tag: 'ul',
15121 //                    cls: 'typeahead typeahead-long dropdown-menu',
15122 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15123 //                }
15124             ]
15125         };
15126         
15127         if(this.hasFeedback && !this.allowBlank){
15128             
15129             var feedback = {
15130                 tag: 'span',
15131                 cls: 'glyphicon form-control-feedback'
15132             };
15133
15134             combobox.cn.push(feedback);
15135         }
15136         
15137         
15138         
15139         var indicator = {
15140             tag : 'i',
15141             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15142             tooltip : 'This field is required'
15143         };
15144         if (Roo.bootstrap.version == 4) {
15145             indicator = {
15146                 tag : 'i',
15147                 style : 'display:none'
15148             };
15149         }
15150         if (align ==='left' && this.fieldLabel.length) {
15151             
15152             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15153             
15154             cfg.cn = [
15155                 indicator,
15156                 {
15157                     tag: 'label',
15158                     'for' :  id,
15159                     cls : 'control-label col-form-label',
15160                     html : this.fieldLabel
15161
15162                 },
15163                 {
15164                     cls : "", 
15165                     cn: [
15166                         combobox
15167                     ]
15168                 }
15169
15170             ];
15171             
15172             var labelCfg = cfg.cn[1];
15173             var contentCfg = cfg.cn[2];
15174             
15175
15176             if(this.indicatorpos == 'right'){
15177                 
15178                 cfg.cn = [
15179                     {
15180                         tag: 'label',
15181                         'for' :  id,
15182                         cls : 'control-label col-form-label',
15183                         cn : [
15184                             {
15185                                 tag : 'span',
15186                                 html : this.fieldLabel
15187                             },
15188                             indicator
15189                         ]
15190                     },
15191                     {
15192                         cls : "",
15193                         cn: [
15194                             combobox
15195                         ]
15196                     }
15197
15198                 ];
15199                 
15200                 
15201                 
15202                 labelCfg = cfg.cn[0];
15203                 contentCfg = cfg.cn[1];
15204             
15205             }
15206             
15207             if(this.labelWidth > 12){
15208                 labelCfg.style = "width: " + this.labelWidth + 'px';
15209             }
15210             
15211             if(this.labelWidth < 13 && this.labelmd == 0){
15212                 this.labelmd = this.labelWidth;
15213             }
15214             
15215             if(this.labellg > 0){
15216                 labelCfg.cls += ' col-lg-' + this.labellg;
15217                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15218             }
15219             
15220             if(this.labelmd > 0){
15221                 labelCfg.cls += ' col-md-' + this.labelmd;
15222                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15223             }
15224             
15225             if(this.labelsm > 0){
15226                 labelCfg.cls += ' col-sm-' + this.labelsm;
15227                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15228             }
15229             
15230             if(this.labelxs > 0){
15231                 labelCfg.cls += ' col-xs-' + this.labelxs;
15232                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15233             }
15234                 
15235                 
15236         } else if ( this.fieldLabel.length) {
15237 //                Roo.log(" label");
15238                  cfg.cn = [
15239                    indicator,
15240                     {
15241                         tag: 'label',
15242                         //cls : 'input-group-addon',
15243                         html : this.fieldLabel
15244                     },
15245                     combobox
15246                 ];
15247                 
15248                 if(this.indicatorpos == 'right'){
15249                     cfg.cn = [
15250                         {
15251                             tag: 'label',
15252                             //cls : 'input-group-addon',
15253                             html : this.fieldLabel
15254                         },
15255                         indicator,
15256                         combobox
15257                     ];
15258                     
15259                 }
15260
15261         } else {
15262             
15263 //                Roo.log(" no label && no align");
15264                 cfg = combobox
15265                      
15266                 
15267         }
15268          
15269         var settings=this;
15270         ['xs','sm','md','lg'].map(function(size){
15271             if (settings[size]) {
15272                 cfg.cls += ' col-' + size + '-' + settings[size];
15273             }
15274         });
15275         
15276         return cfg;
15277         
15278     },
15279     
15280     _initEventsCalled : false,
15281     
15282     // private
15283     initEvents: function()
15284     {   
15285         if (this._initEventsCalled) { // as we call render... prevent looping...
15286             return;
15287         }
15288         this._initEventsCalled = true;
15289         
15290         if (!this.store) {
15291             throw "can not find store for combo";
15292         }
15293         
15294         this.indicator = this.indicatorEl();
15295         
15296         this.store = Roo.factory(this.store, Roo.data);
15297         this.store.parent = this;
15298         
15299         // if we are building from html. then this element is so complex, that we can not really
15300         // use the rendered HTML.
15301         // so we have to trash and replace the previous code.
15302         if (Roo.XComponent.build_from_html) {
15303             // remove this element....
15304             var e = this.el.dom, k=0;
15305             while (e ) { e = e.previousSibling;  ++k;}
15306
15307             this.el.remove();
15308             
15309             this.el=false;
15310             this.rendered = false;
15311             
15312             this.render(this.parent().getChildContainer(true), k);
15313         }
15314         
15315         if(Roo.isIOS && this.useNativeIOS){
15316             this.initIOSView();
15317             return;
15318         }
15319         
15320         /*
15321          * Touch Devices
15322          */
15323         
15324         if(Roo.isTouch && this.mobileTouchView){
15325             this.initTouchView();
15326             return;
15327         }
15328         
15329         if(this.tickable){
15330             this.initTickableEvents();
15331             return;
15332         }
15333         
15334         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15335         
15336         if(this.hiddenName){
15337             
15338             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15339             
15340             this.hiddenField.dom.value =
15341                 this.hiddenValue !== undefined ? this.hiddenValue :
15342                 this.value !== undefined ? this.value : '';
15343
15344             // prevent input submission
15345             this.el.dom.removeAttribute('name');
15346             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15347              
15348              
15349         }
15350         //if(Roo.isGecko){
15351         //    this.el.dom.setAttribute('autocomplete', 'off');
15352         //}
15353         
15354         var cls = 'x-combo-list';
15355         
15356         //this.list = new Roo.Layer({
15357         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15358         //});
15359         
15360         var _this = this;
15361         
15362         (function(){
15363             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15364             _this.list.setWidth(lw);
15365         }).defer(100);
15366         
15367         this.list.on('mouseover', this.onViewOver, this);
15368         this.list.on('mousemove', this.onViewMove, this);
15369         this.list.on('scroll', this.onViewScroll, this);
15370         
15371         /*
15372         this.list.swallowEvent('mousewheel');
15373         this.assetHeight = 0;
15374
15375         if(this.title){
15376             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15377             this.assetHeight += this.header.getHeight();
15378         }
15379
15380         this.innerList = this.list.createChild({cls:cls+'-inner'});
15381         this.innerList.on('mouseover', this.onViewOver, this);
15382         this.innerList.on('mousemove', this.onViewMove, this);
15383         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15384         
15385         if(this.allowBlank && !this.pageSize && !this.disableClear){
15386             this.footer = this.list.createChild({cls:cls+'-ft'});
15387             this.pageTb = new Roo.Toolbar(this.footer);
15388            
15389         }
15390         if(this.pageSize){
15391             this.footer = this.list.createChild({cls:cls+'-ft'});
15392             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15393                     {pageSize: this.pageSize});
15394             
15395         }
15396         
15397         if (this.pageTb && this.allowBlank && !this.disableClear) {
15398             var _this = this;
15399             this.pageTb.add(new Roo.Toolbar.Fill(), {
15400                 cls: 'x-btn-icon x-btn-clear',
15401                 text: '&#160;',
15402                 handler: function()
15403                 {
15404                     _this.collapse();
15405                     _this.clearValue();
15406                     _this.onSelect(false, -1);
15407                 }
15408             });
15409         }
15410         if (this.footer) {
15411             this.assetHeight += this.footer.getHeight();
15412         }
15413         */
15414             
15415         if(!this.tpl){
15416             this.tpl = Roo.bootstrap.version == 4 ?
15417                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15418                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15419         }
15420
15421         this.view = new Roo.View(this.list, this.tpl, {
15422             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15423         });
15424         //this.view.wrapEl.setDisplayed(false);
15425         this.view.on('click', this.onViewClick, this);
15426         
15427         
15428         this.store.on('beforeload', this.onBeforeLoad, this);
15429         this.store.on('load', this.onLoad, this);
15430         this.store.on('loadexception', this.onLoadException, this);
15431         /*
15432         if(this.resizable){
15433             this.resizer = new Roo.Resizable(this.list,  {
15434                pinned:true, handles:'se'
15435             });
15436             this.resizer.on('resize', function(r, w, h){
15437                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15438                 this.listWidth = w;
15439                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15440                 this.restrictHeight();
15441             }, this);
15442             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15443         }
15444         */
15445         if(!this.editable){
15446             this.editable = true;
15447             this.setEditable(false);
15448         }
15449         
15450         /*
15451         
15452         if (typeof(this.events.add.listeners) != 'undefined') {
15453             
15454             this.addicon = this.wrap.createChild(
15455                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15456        
15457             this.addicon.on('click', function(e) {
15458                 this.fireEvent('add', this);
15459             }, this);
15460         }
15461         if (typeof(this.events.edit.listeners) != 'undefined') {
15462             
15463             this.editicon = this.wrap.createChild(
15464                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15465             if (this.addicon) {
15466                 this.editicon.setStyle('margin-left', '40px');
15467             }
15468             this.editicon.on('click', function(e) {
15469                 
15470                 // we fire even  if inothing is selected..
15471                 this.fireEvent('edit', this, this.lastData );
15472                 
15473             }, this);
15474         }
15475         */
15476         
15477         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15478             "up" : function(e){
15479                 this.inKeyMode = true;
15480                 this.selectPrev();
15481             },
15482
15483             "down" : function(e){
15484                 if(!this.isExpanded()){
15485                     this.onTriggerClick();
15486                 }else{
15487                     this.inKeyMode = true;
15488                     this.selectNext();
15489                 }
15490             },
15491
15492             "enter" : function(e){
15493 //                this.onViewClick();
15494                 //return true;
15495                 this.collapse();
15496                 
15497                 if(this.fireEvent("specialkey", this, e)){
15498                     this.onViewClick(false);
15499                 }
15500                 
15501                 return true;
15502             },
15503
15504             "esc" : function(e){
15505                 this.collapse();
15506             },
15507
15508             "tab" : function(e){
15509                 this.collapse();
15510                 
15511                 if(this.fireEvent("specialkey", this, e)){
15512                     this.onViewClick(false);
15513                 }
15514                 
15515                 return true;
15516             },
15517
15518             scope : this,
15519
15520             doRelay : function(foo, bar, hname){
15521                 if(hname == 'down' || this.scope.isExpanded()){
15522                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15523                 }
15524                 return true;
15525             },
15526
15527             forceKeyDown: true
15528         });
15529         
15530         
15531         this.queryDelay = Math.max(this.queryDelay || 10,
15532                 this.mode == 'local' ? 10 : 250);
15533         
15534         
15535         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15536         
15537         if(this.typeAhead){
15538             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15539         }
15540         if(this.editable !== false){
15541             this.inputEl().on("keyup", this.onKeyUp, this);
15542         }
15543         if(this.forceSelection){
15544             this.inputEl().on('blur', this.doForce, this);
15545         }
15546         
15547         if(this.multiple){
15548             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15549             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15550         }
15551     },
15552     
15553     initTickableEvents: function()
15554     {   
15555         this.createList();
15556         
15557         if(this.hiddenName){
15558             
15559             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15560             
15561             this.hiddenField.dom.value =
15562                 this.hiddenValue !== undefined ? this.hiddenValue :
15563                 this.value !== undefined ? this.value : '';
15564
15565             // prevent input submission
15566             this.el.dom.removeAttribute('name');
15567             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15568              
15569              
15570         }
15571         
15572 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15573         
15574         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15575         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15576         if(this.triggerList){
15577             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15578         }
15579          
15580         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15581         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15582         
15583         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15584         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15585         
15586         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15587         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15588         
15589         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15590         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15591         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15592         
15593         this.okBtn.hide();
15594         this.cancelBtn.hide();
15595         
15596         var _this = this;
15597         
15598         (function(){
15599             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15600             _this.list.setWidth(lw);
15601         }).defer(100);
15602         
15603         this.list.on('mouseover', this.onViewOver, this);
15604         this.list.on('mousemove', this.onViewMove, this);
15605         
15606         this.list.on('scroll', this.onViewScroll, this);
15607         
15608         if(!this.tpl){
15609             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15610                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15611         }
15612
15613         this.view = new Roo.View(this.list, this.tpl, {
15614             singleSelect:true,
15615             tickable:true,
15616             parent:this,
15617             store: this.store,
15618             selectedClass: this.selectedClass
15619         });
15620         
15621         //this.view.wrapEl.setDisplayed(false);
15622         this.view.on('click', this.onViewClick, this);
15623         
15624         
15625         
15626         this.store.on('beforeload', this.onBeforeLoad, this);
15627         this.store.on('load', this.onLoad, this);
15628         this.store.on('loadexception', this.onLoadException, this);
15629         
15630         if(this.editable){
15631             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15632                 "up" : function(e){
15633                     this.inKeyMode = true;
15634                     this.selectPrev();
15635                 },
15636
15637                 "down" : function(e){
15638                     this.inKeyMode = true;
15639                     this.selectNext();
15640                 },
15641
15642                 "enter" : function(e){
15643                     if(this.fireEvent("specialkey", this, e)){
15644                         this.onViewClick(false);
15645                     }
15646                     
15647                     return true;
15648                 },
15649
15650                 "esc" : function(e){
15651                     this.onTickableFooterButtonClick(e, false, false);
15652                 },
15653
15654                 "tab" : function(e){
15655                     this.fireEvent("specialkey", this, e);
15656                     
15657                     this.onTickableFooterButtonClick(e, false, false);
15658                     
15659                     return true;
15660                 },
15661
15662                 scope : this,
15663
15664                 doRelay : function(e, fn, key){
15665                     if(this.scope.isExpanded()){
15666                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15667                     }
15668                     return true;
15669                 },
15670
15671                 forceKeyDown: true
15672             });
15673         }
15674         
15675         this.queryDelay = Math.max(this.queryDelay || 10,
15676                 this.mode == 'local' ? 10 : 250);
15677         
15678         
15679         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15680         
15681         if(this.typeAhead){
15682             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15683         }
15684         
15685         if(this.editable !== false){
15686             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15687         }
15688         
15689         this.indicator = this.indicatorEl();
15690         
15691         if(this.indicator){
15692             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15693             this.indicator.hide();
15694         }
15695         
15696     },
15697
15698     onDestroy : function(){
15699         if(this.view){
15700             this.view.setStore(null);
15701             this.view.el.removeAllListeners();
15702             this.view.el.remove();
15703             this.view.purgeListeners();
15704         }
15705         if(this.list){
15706             this.list.dom.innerHTML  = '';
15707         }
15708         
15709         if(this.store){
15710             this.store.un('beforeload', this.onBeforeLoad, this);
15711             this.store.un('load', this.onLoad, this);
15712             this.store.un('loadexception', this.onLoadException, this);
15713         }
15714         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15715     },
15716
15717     // private
15718     fireKey : function(e){
15719         if(e.isNavKeyPress() && !this.list.isVisible()){
15720             this.fireEvent("specialkey", this, e);
15721         }
15722     },
15723
15724     // private
15725     onResize: function(w, h){
15726 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15727 //        
15728 //        if(typeof w != 'number'){
15729 //            // we do not handle it!?!?
15730 //            return;
15731 //        }
15732 //        var tw = this.trigger.getWidth();
15733 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15734 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15735 //        var x = w - tw;
15736 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15737 //            
15738 //        //this.trigger.setStyle('left', x+'px');
15739 //        
15740 //        if(this.list && this.listWidth === undefined){
15741 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15742 //            this.list.setWidth(lw);
15743 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15744 //        }
15745         
15746     
15747         
15748     },
15749
15750     /**
15751      * Allow or prevent the user from directly editing the field text.  If false is passed,
15752      * the user will only be able to select from the items defined in the dropdown list.  This method
15753      * is the runtime equivalent of setting the 'editable' config option at config time.
15754      * @param {Boolean} value True to allow the user to directly edit the field text
15755      */
15756     setEditable : function(value){
15757         if(value == this.editable){
15758             return;
15759         }
15760         this.editable = value;
15761         if(!value){
15762             this.inputEl().dom.setAttribute('readOnly', true);
15763             this.inputEl().on('mousedown', this.onTriggerClick,  this);
15764             this.inputEl().addClass('x-combo-noedit');
15765         }else{
15766             this.inputEl().dom.setAttribute('readOnly', false);
15767             this.inputEl().un('mousedown', this.onTriggerClick,  this);
15768             this.inputEl().removeClass('x-combo-noedit');
15769         }
15770     },
15771
15772     // private
15773     
15774     onBeforeLoad : function(combo,opts){
15775         if(!this.hasFocus){
15776             return;
15777         }
15778          if (!opts.add) {
15779             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15780          }
15781         this.restrictHeight();
15782         this.selectedIndex = -1;
15783     },
15784
15785     // private
15786     onLoad : function(){
15787         
15788         this.hasQuery = false;
15789         
15790         if(!this.hasFocus){
15791             return;
15792         }
15793         
15794         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15795             this.loading.hide();
15796         }
15797         
15798         if(this.store.getCount() > 0){
15799             
15800             this.expand();
15801             this.restrictHeight();
15802             if(this.lastQuery == this.allQuery){
15803                 if(this.editable && !this.tickable){
15804                     this.inputEl().dom.select();
15805                 }
15806                 
15807                 if(
15808                     !this.selectByValue(this.value, true) &&
15809                     this.autoFocus && 
15810                     (
15811                         !this.store.lastOptions ||
15812                         typeof(this.store.lastOptions.add) == 'undefined' || 
15813                         this.store.lastOptions.add != true
15814                     )
15815                 ){
15816                     this.select(0, true);
15817                 }
15818             }else{
15819                 if(this.autoFocus){
15820                     this.selectNext();
15821                 }
15822                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15823                     this.taTask.delay(this.typeAheadDelay);
15824                 }
15825             }
15826         }else{
15827             this.onEmptyResults();
15828         }
15829         
15830         //this.el.focus();
15831     },
15832     // private
15833     onLoadException : function()
15834     {
15835         this.hasQuery = false;
15836         
15837         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15838             this.loading.hide();
15839         }
15840         
15841         if(this.tickable && this.editable){
15842             return;
15843         }
15844         
15845         this.collapse();
15846         // only causes errors at present
15847         //Roo.log(this.store.reader.jsonData);
15848         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15849             // fixme
15850             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15851         //}
15852         
15853         
15854     },
15855     // private
15856     onTypeAhead : function(){
15857         if(this.store.getCount() > 0){
15858             var r = this.store.getAt(0);
15859             var newValue = r.data[this.displayField];
15860             var len = newValue.length;
15861             var selStart = this.getRawValue().length;
15862             
15863             if(selStart != len){
15864                 this.setRawValue(newValue);
15865                 this.selectText(selStart, newValue.length);
15866             }
15867         }
15868     },
15869
15870     // private
15871     onSelect : function(record, index){
15872         
15873         if(this.fireEvent('beforeselect', this, record, index) !== false){
15874         
15875             this.setFromData(index > -1 ? record.data : false);
15876             
15877             this.collapse();
15878             this.fireEvent('select', this, record, index);
15879         }
15880     },
15881
15882     /**
15883      * Returns the currently selected field value or empty string if no value is set.
15884      * @return {String} value The selected value
15885      */
15886     getValue : function()
15887     {
15888         if(Roo.isIOS && this.useNativeIOS){
15889             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15890         }
15891         
15892         if(this.multiple){
15893             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
15894         }
15895         
15896         if(this.valueField){
15897             return typeof this.value != 'undefined' ? this.value : '';
15898         }else{
15899             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
15900         }
15901     },
15902     
15903     getRawValue : function()
15904     {
15905         if(Roo.isIOS && this.useNativeIOS){
15906             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
15907         }
15908         
15909         var v = this.inputEl().getValue();
15910         
15911         return v;
15912     },
15913
15914     /**
15915      * Clears any text/value currently set in the field
15916      */
15917     clearValue : function(){
15918         
15919         if(this.hiddenField){
15920             this.hiddenField.dom.value = '';
15921         }
15922         this.value = '';
15923         this.setRawValue('');
15924         this.lastSelectionText = '';
15925         this.lastData = false;
15926         
15927         var close = this.closeTriggerEl();
15928         
15929         if(close){
15930             close.hide();
15931         }
15932         
15933         this.validate();
15934         
15935     },
15936
15937     /**
15938      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
15939      * will be displayed in the field.  If the value does not match the data value of an existing item,
15940      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
15941      * Otherwise the field will be blank (although the value will still be set).
15942      * @param {String} value The value to match
15943      */
15944     setValue : function(v)
15945     {
15946         if(Roo.isIOS && this.useNativeIOS){
15947             this.setIOSValue(v);
15948             return;
15949         }
15950         
15951         if(this.multiple){
15952             this.syncValue();
15953             return;
15954         }
15955         
15956         var text = v;
15957         if(this.valueField){
15958             var r = this.findRecord(this.valueField, v);
15959             if(r){
15960                 text = r.data[this.displayField];
15961             }else if(this.valueNotFoundText !== undefined){
15962                 text = this.valueNotFoundText;
15963             }
15964         }
15965         this.lastSelectionText = text;
15966         if(this.hiddenField){
15967             this.hiddenField.dom.value = v;
15968         }
15969         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
15970         this.value = v;
15971         
15972         var close = this.closeTriggerEl();
15973         
15974         if(close){
15975             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
15976         }
15977         
15978         this.validate();
15979     },
15980     /**
15981      * @property {Object} the last set data for the element
15982      */
15983     
15984     lastData : false,
15985     /**
15986      * Sets the value of the field based on a object which is related to the record format for the store.
15987      * @param {Object} value the value to set as. or false on reset?
15988      */
15989     setFromData : function(o){
15990         
15991         if(this.multiple){
15992             this.addItem(o);
15993             return;
15994         }
15995             
15996         var dv = ''; // display value
15997         var vv = ''; // value value..
15998         this.lastData = o;
15999         if (this.displayField) {
16000             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16001         } else {
16002             // this is an error condition!!!
16003             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16004         }
16005         
16006         if(this.valueField){
16007             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16008         }
16009         
16010         var close = this.closeTriggerEl();
16011         
16012         if(close){
16013             if(dv.length || vv * 1 > 0){
16014                 close.show() ;
16015                 this.blockFocus=true;
16016             } else {
16017                 close.hide();
16018             }             
16019         }
16020         
16021         if(this.hiddenField){
16022             this.hiddenField.dom.value = vv;
16023             
16024             this.lastSelectionText = dv;
16025             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16026             this.value = vv;
16027             return;
16028         }
16029         // no hidden field.. - we store the value in 'value', but still display
16030         // display field!!!!
16031         this.lastSelectionText = dv;
16032         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16033         this.value = vv;
16034         
16035         
16036         
16037     },
16038     // private
16039     reset : function(){
16040         // overridden so that last data is reset..
16041         
16042         if(this.multiple){
16043             this.clearItem();
16044             return;
16045         }
16046         
16047         this.setValue(this.originalValue);
16048         //this.clearInvalid();
16049         this.lastData = false;
16050         if (this.view) {
16051             this.view.clearSelections();
16052         }
16053         
16054         this.validate();
16055     },
16056     // private
16057     findRecord : function(prop, value){
16058         var record;
16059         if(this.store.getCount() > 0){
16060             this.store.each(function(r){
16061                 if(r.data[prop] == value){
16062                     record = r;
16063                     return false;
16064                 }
16065                 return true;
16066             });
16067         }
16068         return record;
16069     },
16070     
16071     getName: function()
16072     {
16073         // returns hidden if it's set..
16074         if (!this.rendered) {return ''};
16075         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16076         
16077     },
16078     // private
16079     onViewMove : function(e, t){
16080         this.inKeyMode = false;
16081     },
16082
16083     // private
16084     onViewOver : function(e, t){
16085         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16086             return;
16087         }
16088         var item = this.view.findItemFromChild(t);
16089         
16090         if(item){
16091             var index = this.view.indexOf(item);
16092             this.select(index, false);
16093         }
16094     },
16095
16096     // private
16097     onViewClick : function(view, doFocus, el, e)
16098     {
16099         var index = this.view.getSelectedIndexes()[0];
16100         
16101         var r = this.store.getAt(index);
16102         
16103         if(this.tickable){
16104             
16105             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16106                 return;
16107             }
16108             
16109             var rm = false;
16110             var _this = this;
16111             
16112             Roo.each(this.tickItems, function(v,k){
16113                 
16114                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16115                     Roo.log(v);
16116                     _this.tickItems.splice(k, 1);
16117                     
16118                     if(typeof(e) == 'undefined' && view == false){
16119                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16120                     }
16121                     
16122                     rm = true;
16123                     return;
16124                 }
16125             });
16126             
16127             if(rm){
16128                 return;
16129             }
16130             
16131             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16132                 this.tickItems.push(r.data);
16133             }
16134             
16135             if(typeof(e) == 'undefined' && view == false){
16136                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16137             }
16138                     
16139             return;
16140         }
16141         
16142         if(r){
16143             this.onSelect(r, index);
16144         }
16145         if(doFocus !== false && !this.blockFocus){
16146             this.inputEl().focus();
16147         }
16148     },
16149
16150     // private
16151     restrictHeight : function(){
16152         //this.innerList.dom.style.height = '';
16153         //var inner = this.innerList.dom;
16154         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16155         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16156         //this.list.beginUpdate();
16157         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16158         this.list.alignTo(this.inputEl(), this.listAlign);
16159         this.list.alignTo(this.inputEl(), this.listAlign);
16160         //this.list.endUpdate();
16161     },
16162
16163     // private
16164     onEmptyResults : function(){
16165         
16166         if(this.tickable && this.editable){
16167             this.hasFocus = false;
16168             this.restrictHeight();
16169             return;
16170         }
16171         
16172         this.collapse();
16173     },
16174
16175     /**
16176      * Returns true if the dropdown list is expanded, else false.
16177      */
16178     isExpanded : function(){
16179         return this.list.isVisible();
16180     },
16181
16182     /**
16183      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16184      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16185      * @param {String} value The data value of the item to select
16186      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16187      * selected item if it is not currently in view (defaults to true)
16188      * @return {Boolean} True if the value matched an item in the list, else false
16189      */
16190     selectByValue : function(v, scrollIntoView){
16191         if(v !== undefined && v !== null){
16192             var r = this.findRecord(this.valueField || this.displayField, v);
16193             if(r){
16194                 this.select(this.store.indexOf(r), scrollIntoView);
16195                 return true;
16196             }
16197         }
16198         return false;
16199     },
16200
16201     /**
16202      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16203      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16204      * @param {Number} index The zero-based index of the list item to select
16205      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16206      * selected item if it is not currently in view (defaults to true)
16207      */
16208     select : function(index, scrollIntoView){
16209         this.selectedIndex = index;
16210         this.view.select(index);
16211         if(scrollIntoView !== false){
16212             var el = this.view.getNode(index);
16213             /*
16214              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16215              */
16216             if(el){
16217                 this.list.scrollChildIntoView(el, false);
16218             }
16219         }
16220     },
16221
16222     // private
16223     selectNext : function(){
16224         var ct = this.store.getCount();
16225         if(ct > 0){
16226             if(this.selectedIndex == -1){
16227                 this.select(0);
16228             }else if(this.selectedIndex < ct-1){
16229                 this.select(this.selectedIndex+1);
16230             }
16231         }
16232     },
16233
16234     // private
16235     selectPrev : function(){
16236         var ct = this.store.getCount();
16237         if(ct > 0){
16238             if(this.selectedIndex == -1){
16239                 this.select(0);
16240             }else if(this.selectedIndex != 0){
16241                 this.select(this.selectedIndex-1);
16242             }
16243         }
16244     },
16245
16246     // private
16247     onKeyUp : function(e){
16248         if(this.editable !== false && !e.isSpecialKey()){
16249             this.lastKey = e.getKey();
16250             this.dqTask.delay(this.queryDelay);
16251         }
16252     },
16253
16254     // private
16255     validateBlur : function(){
16256         return !this.list || !this.list.isVisible();   
16257     },
16258
16259     // private
16260     initQuery : function(){
16261         
16262         var v = this.getRawValue();
16263         
16264         if(this.tickable && this.editable){
16265             v = this.tickableInputEl().getValue();
16266         }
16267         
16268         this.doQuery(v);
16269     },
16270
16271     // private
16272     doForce : function(){
16273         if(this.inputEl().dom.value.length > 0){
16274             this.inputEl().dom.value =
16275                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16276              
16277         }
16278     },
16279
16280     /**
16281      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16282      * query allowing the query action to be canceled if needed.
16283      * @param {String} query The SQL query to execute
16284      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16285      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16286      * saved in the current store (defaults to false)
16287      */
16288     doQuery : function(q, forceAll){
16289         
16290         if(q === undefined || q === null){
16291             q = '';
16292         }
16293         var qe = {
16294             query: q,
16295             forceAll: forceAll,
16296             combo: this,
16297             cancel:false
16298         };
16299         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16300             return false;
16301         }
16302         q = qe.query;
16303         
16304         forceAll = qe.forceAll;
16305         if(forceAll === true || (q.length >= this.minChars)){
16306             
16307             this.hasQuery = true;
16308             
16309             if(this.lastQuery != q || this.alwaysQuery){
16310                 this.lastQuery = q;
16311                 if(this.mode == 'local'){
16312                     this.selectedIndex = -1;
16313                     if(forceAll){
16314                         this.store.clearFilter();
16315                     }else{
16316                         
16317                         if(this.specialFilter){
16318                             this.fireEvent('specialfilter', this);
16319                             this.onLoad();
16320                             return;
16321                         }
16322                         
16323                         this.store.filter(this.displayField, q);
16324                     }
16325                     
16326                     this.store.fireEvent("datachanged", this.store);
16327                     
16328                     this.onLoad();
16329                     
16330                     
16331                 }else{
16332                     
16333                     this.store.baseParams[this.queryParam] = q;
16334                     
16335                     var options = {params : this.getParams(q)};
16336                     
16337                     if(this.loadNext){
16338                         options.add = true;
16339                         options.params.start = this.page * this.pageSize;
16340                     }
16341                     
16342                     this.store.load(options);
16343                     
16344                     /*
16345                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16346                      *  we should expand the list on onLoad
16347                      *  so command out it
16348                      */
16349 //                    this.expand();
16350                 }
16351             }else{
16352                 this.selectedIndex = -1;
16353                 this.onLoad();   
16354             }
16355         }
16356         
16357         this.loadNext = false;
16358     },
16359     
16360     // private
16361     getParams : function(q){
16362         var p = {};
16363         //p[this.queryParam] = q;
16364         
16365         if(this.pageSize){
16366             p.start = 0;
16367             p.limit = this.pageSize;
16368         }
16369         return p;
16370     },
16371
16372     /**
16373      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16374      */
16375     collapse : function(){
16376         if(!this.isExpanded()){
16377             return;
16378         }
16379         
16380         this.list.hide();
16381         
16382         this.hasFocus = false;
16383         
16384         if(this.tickable){
16385             this.okBtn.hide();
16386             this.cancelBtn.hide();
16387             this.trigger.show();
16388             
16389             if(this.editable){
16390                 this.tickableInputEl().dom.value = '';
16391                 this.tickableInputEl().blur();
16392             }
16393             
16394         }
16395         
16396         Roo.get(document).un('mousedown', this.collapseIf, this);
16397         Roo.get(document).un('mousewheel', this.collapseIf, this);
16398         if (!this.editable) {
16399             Roo.get(document).un('keydown', this.listKeyPress, this);
16400         }
16401         this.fireEvent('collapse', this);
16402         
16403         this.validate();
16404     },
16405
16406     // private
16407     collapseIf : function(e){
16408         var in_combo  = e.within(this.el);
16409         var in_list =  e.within(this.list);
16410         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16411         
16412         if (in_combo || in_list || is_list) {
16413             //e.stopPropagation();
16414             return;
16415         }
16416         
16417         if(this.tickable){
16418             this.onTickableFooterButtonClick(e, false, false);
16419         }
16420
16421         this.collapse();
16422         
16423     },
16424
16425     /**
16426      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16427      */
16428     expand : function(){
16429        
16430         if(this.isExpanded() || !this.hasFocus){
16431             return;
16432         }
16433         
16434         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16435         this.list.setWidth(lw);
16436         
16437         Roo.log('expand');
16438         
16439         this.list.show();
16440         
16441         this.restrictHeight();
16442         
16443         if(this.tickable){
16444             
16445             this.tickItems = Roo.apply([], this.item);
16446             
16447             this.okBtn.show();
16448             this.cancelBtn.show();
16449             this.trigger.hide();
16450             
16451             if(this.editable){
16452                 this.tickableInputEl().focus();
16453             }
16454             
16455         }
16456         
16457         Roo.get(document).on('mousedown', this.collapseIf, this);
16458         Roo.get(document).on('mousewheel', this.collapseIf, this);
16459         if (!this.editable) {
16460             Roo.get(document).on('keydown', this.listKeyPress, this);
16461         }
16462         
16463         this.fireEvent('expand', this);
16464     },
16465
16466     // private
16467     // Implements the default empty TriggerField.onTriggerClick function
16468     onTriggerClick : function(e)
16469     {
16470         Roo.log('trigger click');
16471         
16472         if(this.disabled || !this.triggerList){
16473             return;
16474         }
16475         
16476         this.page = 0;
16477         this.loadNext = false;
16478         
16479         if(this.isExpanded()){
16480             this.collapse();
16481             if (!this.blockFocus) {
16482                 this.inputEl().focus();
16483             }
16484             
16485         }else {
16486             this.hasFocus = true;
16487             if(this.triggerAction == 'all') {
16488                 this.doQuery(this.allQuery, true);
16489             } else {
16490                 this.doQuery(this.getRawValue());
16491             }
16492             if (!this.blockFocus) {
16493                 this.inputEl().focus();
16494             }
16495         }
16496     },
16497     
16498     onTickableTriggerClick : function(e)
16499     {
16500         if(this.disabled){
16501             return;
16502         }
16503         
16504         this.page = 0;
16505         this.loadNext = false;
16506         this.hasFocus = true;
16507         
16508         if(this.triggerAction == 'all') {
16509             this.doQuery(this.allQuery, true);
16510         } else {
16511             this.doQuery(this.getRawValue());
16512         }
16513     },
16514     
16515     onSearchFieldClick : function(e)
16516     {
16517         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16518             this.onTickableFooterButtonClick(e, false, false);
16519             return;
16520         }
16521         
16522         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16523             return;
16524         }
16525         
16526         this.page = 0;
16527         this.loadNext = false;
16528         this.hasFocus = true;
16529         
16530         if(this.triggerAction == 'all') {
16531             this.doQuery(this.allQuery, true);
16532         } else {
16533             this.doQuery(this.getRawValue());
16534         }
16535     },
16536     
16537     listKeyPress : function(e)
16538     {
16539         //Roo.log('listkeypress');
16540         // scroll to first matching element based on key pres..
16541         if (e.isSpecialKey()) {
16542             return false;
16543         }
16544         var k = String.fromCharCode(e.getKey()).toUpperCase();
16545         //Roo.log(k);
16546         var match  = false;
16547         var csel = this.view.getSelectedNodes();
16548         var cselitem = false;
16549         if (csel.length) {
16550             var ix = this.view.indexOf(csel[0]);
16551             cselitem  = this.store.getAt(ix);
16552             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16553                 cselitem = false;
16554             }
16555             
16556         }
16557         
16558         this.store.each(function(v) { 
16559             if (cselitem) {
16560                 // start at existing selection.
16561                 if (cselitem.id == v.id) {
16562                     cselitem = false;
16563                 }
16564                 return true;
16565             }
16566                 
16567             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16568                 match = this.store.indexOf(v);
16569                 return false;
16570             }
16571             return true;
16572         }, this);
16573         
16574         if (match === false) {
16575             return true; // no more action?
16576         }
16577         // scroll to?
16578         this.view.select(match);
16579         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16580         sn.scrollIntoView(sn.dom.parentNode, false);
16581     },
16582     
16583     onViewScroll : function(e, t){
16584         
16585         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){
16586             return;
16587         }
16588         
16589         this.hasQuery = true;
16590         
16591         this.loading = this.list.select('.loading', true).first();
16592         
16593         if(this.loading === null){
16594             this.list.createChild({
16595                 tag: 'div',
16596                 cls: 'loading roo-select2-more-results roo-select2-active',
16597                 html: 'Loading more results...'
16598             });
16599             
16600             this.loading = this.list.select('.loading', true).first();
16601             
16602             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16603             
16604             this.loading.hide();
16605         }
16606         
16607         this.loading.show();
16608         
16609         var _combo = this;
16610         
16611         this.page++;
16612         this.loadNext = true;
16613         
16614         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16615         
16616         return;
16617     },
16618     
16619     addItem : function(o)
16620     {   
16621         var dv = ''; // display value
16622         
16623         if (this.displayField) {
16624             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16625         } else {
16626             // this is an error condition!!!
16627             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16628         }
16629         
16630         if(!dv.length){
16631             return;
16632         }
16633         
16634         var choice = this.choices.createChild({
16635             tag: 'li',
16636             cls: 'roo-select2-search-choice',
16637             cn: [
16638                 {
16639                     tag: 'div',
16640                     html: dv
16641                 },
16642                 {
16643                     tag: 'a',
16644                     href: '#',
16645                     cls: 'roo-select2-search-choice-close fa fa-times',
16646                     tabindex: '-1'
16647                 }
16648             ]
16649             
16650         }, this.searchField);
16651         
16652         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16653         
16654         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16655         
16656         this.item.push(o);
16657         
16658         this.lastData = o;
16659         
16660         this.syncValue();
16661         
16662         this.inputEl().dom.value = '';
16663         
16664         this.validate();
16665     },
16666     
16667     onRemoveItem : function(e, _self, o)
16668     {
16669         e.preventDefault();
16670         
16671         this.lastItem = Roo.apply([], this.item);
16672         
16673         var index = this.item.indexOf(o.data) * 1;
16674         
16675         if( index < 0){
16676             Roo.log('not this item?!');
16677             return;
16678         }
16679         
16680         this.item.splice(index, 1);
16681         o.item.remove();
16682         
16683         this.syncValue();
16684         
16685         this.fireEvent('remove', this, e);
16686         
16687         this.validate();
16688         
16689     },
16690     
16691     syncValue : function()
16692     {
16693         if(!this.item.length){
16694             this.clearValue();
16695             return;
16696         }
16697             
16698         var value = [];
16699         var _this = this;
16700         Roo.each(this.item, function(i){
16701             if(_this.valueField){
16702                 value.push(i[_this.valueField]);
16703                 return;
16704             }
16705
16706             value.push(i);
16707         });
16708
16709         this.value = value.join(',');
16710
16711         if(this.hiddenField){
16712             this.hiddenField.dom.value = this.value;
16713         }
16714         
16715         this.store.fireEvent("datachanged", this.store);
16716         
16717         this.validate();
16718     },
16719     
16720     clearItem : function()
16721     {
16722         if(!this.multiple){
16723             return;
16724         }
16725         
16726         this.item = [];
16727         
16728         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16729            c.remove();
16730         });
16731         
16732         this.syncValue();
16733         
16734         this.validate();
16735         
16736         if(this.tickable && !Roo.isTouch){
16737             this.view.refresh();
16738         }
16739     },
16740     
16741     inputEl: function ()
16742     {
16743         if(Roo.isIOS && this.useNativeIOS){
16744             return this.el.select('select.roo-ios-select', true).first();
16745         }
16746         
16747         if(Roo.isTouch && this.mobileTouchView){
16748             return this.el.select('input.form-control',true).first();
16749         }
16750         
16751         if(this.tickable){
16752             return this.searchField;
16753         }
16754         
16755         return this.el.select('input.form-control',true).first();
16756     },
16757     
16758     onTickableFooterButtonClick : function(e, btn, el)
16759     {
16760         e.preventDefault();
16761         
16762         this.lastItem = Roo.apply([], this.item);
16763         
16764         if(btn && btn.name == 'cancel'){
16765             this.tickItems = Roo.apply([], this.item);
16766             this.collapse();
16767             return;
16768         }
16769         
16770         this.clearItem();
16771         
16772         var _this = this;
16773         
16774         Roo.each(this.tickItems, function(o){
16775             _this.addItem(o);
16776         });
16777         
16778         this.collapse();
16779         
16780     },
16781     
16782     validate : function()
16783     {
16784         if(this.getVisibilityEl().hasClass('hidden')){
16785             return true;
16786         }
16787         
16788         var v = this.getRawValue();
16789         
16790         if(this.multiple){
16791             v = this.getValue();
16792         }
16793         
16794         if(this.disabled || this.allowBlank || v.length){
16795             this.markValid();
16796             return true;
16797         }
16798         
16799         this.markInvalid();
16800         return false;
16801     },
16802     
16803     tickableInputEl : function()
16804     {
16805         if(!this.tickable || !this.editable){
16806             return this.inputEl();
16807         }
16808         
16809         return this.inputEl().select('.roo-select2-search-field-input', true).first();
16810     },
16811     
16812     
16813     getAutoCreateTouchView : function()
16814     {
16815         var id = Roo.id();
16816         
16817         var cfg = {
16818             cls: 'form-group' //input-group
16819         };
16820         
16821         var input =  {
16822             tag: 'input',
16823             id : id,
16824             type : this.inputType,
16825             cls : 'form-control x-combo-noedit',
16826             autocomplete: 'new-password',
16827             placeholder : this.placeholder || '',
16828             readonly : true
16829         };
16830         
16831         if (this.name) {
16832             input.name = this.name;
16833         }
16834         
16835         if (this.size) {
16836             input.cls += ' input-' + this.size;
16837         }
16838         
16839         if (this.disabled) {
16840             input.disabled = true;
16841         }
16842         
16843         var inputblock = {
16844             cls : '',
16845             cn : [
16846                 input
16847             ]
16848         };
16849         
16850         if(this.before){
16851             inputblock.cls += ' input-group';
16852             
16853             inputblock.cn.unshift({
16854                 tag :'span',
16855                 cls : 'input-group-addon input-group-prepend input-group-text',
16856                 html : this.before
16857             });
16858         }
16859         
16860         if(this.removable && !this.multiple){
16861             inputblock.cls += ' roo-removable';
16862             
16863             inputblock.cn.push({
16864                 tag: 'button',
16865                 html : 'x',
16866                 cls : 'roo-combo-removable-btn close'
16867             });
16868         }
16869
16870         if(this.hasFeedback && !this.allowBlank){
16871             
16872             inputblock.cls += ' has-feedback';
16873             
16874             inputblock.cn.push({
16875                 tag: 'span',
16876                 cls: 'glyphicon form-control-feedback'
16877             });
16878             
16879         }
16880         
16881         if (this.after) {
16882             
16883             inputblock.cls += (this.before) ? '' : ' input-group';
16884             
16885             inputblock.cn.push({
16886                 tag :'span',
16887                 cls : 'input-group-addon input-group-append input-group-text',
16888                 html : this.after
16889             });
16890         }
16891
16892         
16893         var ibwrap = inputblock;
16894         
16895         if(this.multiple){
16896             ibwrap = {
16897                 tag: 'ul',
16898                 cls: 'roo-select2-choices',
16899                 cn:[
16900                     {
16901                         tag: 'li',
16902                         cls: 'roo-select2-search-field',
16903                         cn: [
16904
16905                             inputblock
16906                         ]
16907                     }
16908                 ]
16909             };
16910         
16911             
16912         }
16913         
16914         var combobox = {
16915             cls: 'roo-select2-container input-group roo-touchview-combobox ',
16916             cn: [
16917                 {
16918                     tag: 'input',
16919                     type : 'hidden',
16920                     cls: 'form-hidden-field'
16921                 },
16922                 ibwrap
16923             ]
16924         };
16925         
16926         if(!this.multiple && this.showToggleBtn){
16927             
16928             var caret = {
16929                 cls: 'caret'
16930             };
16931             
16932             if (this.caret != false) {
16933                 caret = {
16934                      tag: 'i',
16935                      cls: 'fa fa-' + this.caret
16936                 };
16937                 
16938             }
16939             
16940             combobox.cn.push({
16941                 tag :'span',
16942                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
16943                 cn : [
16944                     Roo.bootstrap.version == 3 ? caret : '',
16945                     {
16946                         tag: 'span',
16947                         cls: 'combobox-clear',
16948                         cn  : [
16949                             {
16950                                 tag : 'i',
16951                                 cls: 'icon-remove'
16952                             }
16953                         ]
16954                     }
16955                 ]
16956
16957             })
16958         }
16959         
16960         if(this.multiple){
16961             combobox.cls += ' roo-select2-container-multi';
16962         }
16963         
16964         var align = this.labelAlign || this.parentLabelAlign();
16965         
16966         if (align ==='left' && this.fieldLabel.length) {
16967
16968             cfg.cn = [
16969                 {
16970                    tag : 'i',
16971                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
16972                    tooltip : 'This field is required'
16973                 },
16974                 {
16975                     tag: 'label',
16976                     cls : 'control-label col-form-label',
16977                     html : this.fieldLabel
16978
16979                 },
16980                 {
16981                     cls : '', 
16982                     cn: [
16983                         combobox
16984                     ]
16985                 }
16986             ];
16987             
16988             var labelCfg = cfg.cn[1];
16989             var contentCfg = cfg.cn[2];
16990             
16991
16992             if(this.indicatorpos == 'right'){
16993                 cfg.cn = [
16994                     {
16995                         tag: 'label',
16996                         'for' :  id,
16997                         cls : 'control-label col-form-label',
16998                         cn : [
16999                             {
17000                                 tag : 'span',
17001                                 html : this.fieldLabel
17002                             },
17003                             {
17004                                 tag : 'i',
17005                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17006                                 tooltip : 'This field is required'
17007                             }
17008                         ]
17009                     },
17010                     {
17011                         cls : "",
17012                         cn: [
17013                             combobox
17014                         ]
17015                     }
17016
17017                 ];
17018                 
17019                 labelCfg = cfg.cn[0];
17020                 contentCfg = cfg.cn[1];
17021             }
17022             
17023            
17024             
17025             if(this.labelWidth > 12){
17026                 labelCfg.style = "width: " + this.labelWidth + 'px';
17027             }
17028             
17029             if(this.labelWidth < 13 && this.labelmd == 0){
17030                 this.labelmd = this.labelWidth;
17031             }
17032             
17033             if(this.labellg > 0){
17034                 labelCfg.cls += ' col-lg-' + this.labellg;
17035                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17036             }
17037             
17038             if(this.labelmd > 0){
17039                 labelCfg.cls += ' col-md-' + this.labelmd;
17040                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17041             }
17042             
17043             if(this.labelsm > 0){
17044                 labelCfg.cls += ' col-sm-' + this.labelsm;
17045                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17046             }
17047             
17048             if(this.labelxs > 0){
17049                 labelCfg.cls += ' col-xs-' + this.labelxs;
17050                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17051             }
17052                 
17053                 
17054         } else if ( this.fieldLabel.length) {
17055             cfg.cn = [
17056                 {
17057                    tag : 'i',
17058                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17059                    tooltip : 'This field is required'
17060                 },
17061                 {
17062                     tag: 'label',
17063                     cls : 'control-label',
17064                     html : this.fieldLabel
17065
17066                 },
17067                 {
17068                     cls : '', 
17069                     cn: [
17070                         combobox
17071                     ]
17072                 }
17073             ];
17074             
17075             if(this.indicatorpos == 'right'){
17076                 cfg.cn = [
17077                     {
17078                         tag: 'label',
17079                         cls : 'control-label',
17080                         html : this.fieldLabel,
17081                         cn : [
17082                             {
17083                                tag : 'i',
17084                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17085                                tooltip : 'This field is required'
17086                             }
17087                         ]
17088                     },
17089                     {
17090                         cls : '', 
17091                         cn: [
17092                             combobox
17093                         ]
17094                     }
17095                 ];
17096             }
17097         } else {
17098             cfg.cn = combobox;    
17099         }
17100         
17101         
17102         var settings = this;
17103         
17104         ['xs','sm','md','lg'].map(function(size){
17105             if (settings[size]) {
17106                 cfg.cls += ' col-' + size + '-' + settings[size];
17107             }
17108         });
17109         
17110         return cfg;
17111     },
17112     
17113     initTouchView : function()
17114     {
17115         this.renderTouchView();
17116         
17117         this.touchViewEl.on('scroll', function(){
17118             this.el.dom.scrollTop = 0;
17119         }, this);
17120         
17121         this.originalValue = this.getValue();
17122         
17123         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17124         
17125         this.inputEl().on("click", this.showTouchView, this);
17126         if (this.triggerEl) {
17127             this.triggerEl.on("click", this.showTouchView, this);
17128         }
17129         
17130         
17131         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17132         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17133         
17134         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17135         
17136         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17137         this.store.on('load', this.onTouchViewLoad, this);
17138         this.store.on('loadexception', this.onTouchViewLoadException, this);
17139         
17140         if(this.hiddenName){
17141             
17142             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17143             
17144             this.hiddenField.dom.value =
17145                 this.hiddenValue !== undefined ? this.hiddenValue :
17146                 this.value !== undefined ? this.value : '';
17147         
17148             this.el.dom.removeAttribute('name');
17149             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17150         }
17151         
17152         if(this.multiple){
17153             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17154             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17155         }
17156         
17157         if(this.removable && !this.multiple){
17158             var close = this.closeTriggerEl();
17159             if(close){
17160                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17161                 close.on('click', this.removeBtnClick, this, close);
17162             }
17163         }
17164         /*
17165          * fix the bug in Safari iOS8
17166          */
17167         this.inputEl().on("focus", function(e){
17168             document.activeElement.blur();
17169         }, this);
17170         
17171         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17172         
17173         return;
17174         
17175         
17176     },
17177     
17178     renderTouchView : function()
17179     {
17180         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17181         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17182         
17183         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17184         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17185         
17186         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17187         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17188         this.touchViewBodyEl.setStyle('overflow', 'auto');
17189         
17190         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17191         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17192         
17193         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17194         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17195         
17196     },
17197     
17198     showTouchView : function()
17199     {
17200         if(this.disabled){
17201             return;
17202         }
17203         
17204         this.touchViewHeaderEl.hide();
17205
17206         if(this.modalTitle.length){
17207             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17208             this.touchViewHeaderEl.show();
17209         }
17210
17211         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17212         this.touchViewEl.show();
17213
17214         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17215         
17216         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17217         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17218
17219         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17220
17221         if(this.modalTitle.length){
17222             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17223         }
17224         
17225         this.touchViewBodyEl.setHeight(bodyHeight);
17226
17227         if(this.animate){
17228             var _this = this;
17229             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
17230         }else{
17231             this.touchViewEl.addClass('in');
17232         }
17233         
17234         if(this._touchViewMask){
17235             Roo.get(document.body).addClass("x-body-masked");
17236             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17237             this._touchViewMask.setStyle('z-index', 10000);
17238             this._touchViewMask.addClass('show');
17239         }
17240         
17241         this.doTouchViewQuery();
17242         
17243     },
17244     
17245     hideTouchView : function()
17246     {
17247         this.touchViewEl.removeClass('in');
17248
17249         if(this.animate){
17250             var _this = this;
17251             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17252         }else{
17253             this.touchViewEl.setStyle('display', 'none');
17254         }
17255         
17256         if(this._touchViewMask){
17257             this._touchViewMask.removeClass('show');
17258             Roo.get(document.body).removeClass("x-body-masked");
17259         }
17260     },
17261     
17262     setTouchViewValue : function()
17263     {
17264         if(this.multiple){
17265             this.clearItem();
17266         
17267             var _this = this;
17268
17269             Roo.each(this.tickItems, function(o){
17270                 this.addItem(o);
17271             }, this);
17272         }
17273         
17274         this.hideTouchView();
17275     },
17276     
17277     doTouchViewQuery : function()
17278     {
17279         var qe = {
17280             query: '',
17281             forceAll: true,
17282             combo: this,
17283             cancel:false
17284         };
17285         
17286         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17287             return false;
17288         }
17289         
17290         if(!this.alwaysQuery || this.mode == 'local'){
17291             this.onTouchViewLoad();
17292             return;
17293         }
17294         
17295         this.store.load();
17296     },
17297     
17298     onTouchViewBeforeLoad : function(combo,opts)
17299     {
17300         return;
17301     },
17302
17303     // private
17304     onTouchViewLoad : function()
17305     {
17306         if(this.store.getCount() < 1){
17307             this.onTouchViewEmptyResults();
17308             return;
17309         }
17310         
17311         this.clearTouchView();
17312         
17313         var rawValue = this.getRawValue();
17314         
17315         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17316         
17317         this.tickItems = [];
17318         
17319         this.store.data.each(function(d, rowIndex){
17320             var row = this.touchViewListGroup.createChild(template);
17321             
17322             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17323                 row.addClass(d.data.cls);
17324             }
17325             
17326             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17327                 var cfg = {
17328                     data : d.data,
17329                     html : d.data[this.displayField]
17330                 };
17331                 
17332                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17333                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17334                 }
17335             }
17336             row.removeClass('selected');
17337             if(!this.multiple && this.valueField &&
17338                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17339             {
17340                 // radio buttons..
17341                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17342                 row.addClass('selected');
17343             }
17344             
17345             if(this.multiple && this.valueField &&
17346                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17347             {
17348                 
17349                 // checkboxes...
17350                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17351                 this.tickItems.push(d.data);
17352             }
17353             
17354             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17355             
17356         }, this);
17357         
17358         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17359         
17360         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17361
17362         if(this.modalTitle.length){
17363             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17364         }
17365
17366         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17367         
17368         if(this.mobile_restrict_height && listHeight < bodyHeight){
17369             this.touchViewBodyEl.setHeight(listHeight);
17370         }
17371         
17372         var _this = this;
17373         
17374         if(firstChecked && listHeight > bodyHeight){
17375             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17376         }
17377         
17378     },
17379     
17380     onTouchViewLoadException : function()
17381     {
17382         this.hideTouchView();
17383     },
17384     
17385     onTouchViewEmptyResults : function()
17386     {
17387         this.clearTouchView();
17388         
17389         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17390         
17391         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17392         
17393     },
17394     
17395     clearTouchView : function()
17396     {
17397         this.touchViewListGroup.dom.innerHTML = '';
17398     },
17399     
17400     onTouchViewClick : function(e, el, o)
17401     {
17402         e.preventDefault();
17403         
17404         var row = o.row;
17405         var rowIndex = o.rowIndex;
17406         
17407         var r = this.store.getAt(rowIndex);
17408         
17409         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17410             
17411             if(!this.multiple){
17412                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17413                     c.dom.removeAttribute('checked');
17414                 }, this);
17415
17416                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17417
17418                 this.setFromData(r.data);
17419
17420                 var close = this.closeTriggerEl();
17421
17422                 if(close){
17423                     close.show();
17424                 }
17425
17426                 this.hideTouchView();
17427
17428                 this.fireEvent('select', this, r, rowIndex);
17429
17430                 return;
17431             }
17432
17433             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17434                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17435                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17436                 return;
17437             }
17438
17439             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17440             this.addItem(r.data);
17441             this.tickItems.push(r.data);
17442         }
17443     },
17444     
17445     getAutoCreateNativeIOS : function()
17446     {
17447         var cfg = {
17448             cls: 'form-group' //input-group,
17449         };
17450         
17451         var combobox =  {
17452             tag: 'select',
17453             cls : 'roo-ios-select'
17454         };
17455         
17456         if (this.name) {
17457             combobox.name = this.name;
17458         }
17459         
17460         if (this.disabled) {
17461             combobox.disabled = true;
17462         }
17463         
17464         var settings = this;
17465         
17466         ['xs','sm','md','lg'].map(function(size){
17467             if (settings[size]) {
17468                 cfg.cls += ' col-' + size + '-' + settings[size];
17469             }
17470         });
17471         
17472         cfg.cn = combobox;
17473         
17474         return cfg;
17475         
17476     },
17477     
17478     initIOSView : function()
17479     {
17480         this.store.on('load', this.onIOSViewLoad, this);
17481         
17482         return;
17483     },
17484     
17485     onIOSViewLoad : function()
17486     {
17487         if(this.store.getCount() < 1){
17488             return;
17489         }
17490         
17491         this.clearIOSView();
17492         
17493         if(this.allowBlank) {
17494             
17495             var default_text = '-- SELECT --';
17496             
17497             if(this.placeholder.length){
17498                 default_text = this.placeholder;
17499             }
17500             
17501             if(this.emptyTitle.length){
17502                 default_text += ' - ' + this.emptyTitle + ' -';
17503             }
17504             
17505             var opt = this.inputEl().createChild({
17506                 tag: 'option',
17507                 value : 0,
17508                 html : default_text
17509             });
17510             
17511             var o = {};
17512             o[this.valueField] = 0;
17513             o[this.displayField] = default_text;
17514             
17515             this.ios_options.push({
17516                 data : o,
17517                 el : opt
17518             });
17519             
17520         }
17521         
17522         this.store.data.each(function(d, rowIndex){
17523             
17524             var html = '';
17525             
17526             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17527                 html = d.data[this.displayField];
17528             }
17529             
17530             var value = '';
17531             
17532             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17533                 value = d.data[this.valueField];
17534             }
17535             
17536             var option = {
17537                 tag: 'option',
17538                 value : value,
17539                 html : html
17540             };
17541             
17542             if(this.value == d.data[this.valueField]){
17543                 option['selected'] = true;
17544             }
17545             
17546             var opt = this.inputEl().createChild(option);
17547             
17548             this.ios_options.push({
17549                 data : d.data,
17550                 el : opt
17551             });
17552             
17553         }, this);
17554         
17555         this.inputEl().on('change', function(){
17556            this.fireEvent('select', this);
17557         }, this);
17558         
17559     },
17560     
17561     clearIOSView: function()
17562     {
17563         this.inputEl().dom.innerHTML = '';
17564         
17565         this.ios_options = [];
17566     },
17567     
17568     setIOSValue: function(v)
17569     {
17570         this.value = v;
17571         
17572         if(!this.ios_options){
17573             return;
17574         }
17575         
17576         Roo.each(this.ios_options, function(opts){
17577            
17578            opts.el.dom.removeAttribute('selected');
17579            
17580            if(opts.data[this.valueField] != v){
17581                return;
17582            }
17583            
17584            opts.el.dom.setAttribute('selected', true);
17585            
17586         }, this);
17587     }
17588
17589     /** 
17590     * @cfg {Boolean} grow 
17591     * @hide 
17592     */
17593     /** 
17594     * @cfg {Number} growMin 
17595     * @hide 
17596     */
17597     /** 
17598     * @cfg {Number} growMax 
17599     * @hide 
17600     */
17601     /**
17602      * @hide
17603      * @method autoSize
17604      */
17605 });
17606
17607 Roo.apply(Roo.bootstrap.ComboBox,  {
17608     
17609     header : {
17610         tag: 'div',
17611         cls: 'modal-header',
17612         cn: [
17613             {
17614                 tag: 'h4',
17615                 cls: 'modal-title'
17616             }
17617         ]
17618     },
17619     
17620     body : {
17621         tag: 'div',
17622         cls: 'modal-body',
17623         cn: [
17624             {
17625                 tag: 'ul',
17626                 cls: 'list-group'
17627             }
17628         ]
17629     },
17630     
17631     listItemRadio : {
17632         tag: 'li',
17633         cls: 'list-group-item',
17634         cn: [
17635             {
17636                 tag: 'span',
17637                 cls: 'roo-combobox-list-group-item-value'
17638             },
17639             {
17640                 tag: 'div',
17641                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17642                 cn: [
17643                     {
17644                         tag: 'input',
17645                         type: 'radio'
17646                     },
17647                     {
17648                         tag: 'label'
17649                     }
17650                 ]
17651             }
17652         ]
17653     },
17654     
17655     listItemCheckbox : {
17656         tag: 'li',
17657         cls: 'list-group-item',
17658         cn: [
17659             {
17660                 tag: 'span',
17661                 cls: 'roo-combobox-list-group-item-value'
17662             },
17663             {
17664                 tag: 'div',
17665                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17666                 cn: [
17667                     {
17668                         tag: 'input',
17669                         type: 'checkbox'
17670                     },
17671                     {
17672                         tag: 'label'
17673                     }
17674                 ]
17675             }
17676         ]
17677     },
17678     
17679     emptyResult : {
17680         tag: 'div',
17681         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17682     },
17683     
17684     footer : {
17685         tag: 'div',
17686         cls: 'modal-footer',
17687         cn: [
17688             {
17689                 tag: 'div',
17690                 cls: 'row',
17691                 cn: [
17692                     {
17693                         tag: 'div',
17694                         cls: 'col-xs-6 text-left',
17695                         cn: {
17696                             tag: 'button',
17697                             cls: 'btn btn-danger roo-touch-view-cancel',
17698                             html: 'Cancel'
17699                         }
17700                     },
17701                     {
17702                         tag: 'div',
17703                         cls: 'col-xs-6 text-right',
17704                         cn: {
17705                             tag: 'button',
17706                             cls: 'btn btn-success roo-touch-view-ok',
17707                             html: 'OK'
17708                         }
17709                     }
17710                 ]
17711             }
17712         ]
17713         
17714     }
17715 });
17716
17717 Roo.apply(Roo.bootstrap.ComboBox,  {
17718     
17719     touchViewTemplate : {
17720         tag: 'div',
17721         cls: 'modal fade roo-combobox-touch-view',
17722         cn: [
17723             {
17724                 tag: 'div',
17725                 cls: 'modal-dialog',
17726                 style : 'position:fixed', // we have to fix position....
17727                 cn: [
17728                     {
17729                         tag: 'div',
17730                         cls: 'modal-content',
17731                         cn: [
17732                             Roo.bootstrap.ComboBox.header,
17733                             Roo.bootstrap.ComboBox.body,
17734                             Roo.bootstrap.ComboBox.footer
17735                         ]
17736                     }
17737                 ]
17738             }
17739         ]
17740     }
17741 });/*
17742  * Based on:
17743  * Ext JS Library 1.1.1
17744  * Copyright(c) 2006-2007, Ext JS, LLC.
17745  *
17746  * Originally Released Under LGPL - original licence link has changed is not relivant.
17747  *
17748  * Fork - LGPL
17749  * <script type="text/javascript">
17750  */
17751
17752 /**
17753  * @class Roo.View
17754  * @extends Roo.util.Observable
17755  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
17756  * This class also supports single and multi selection modes. <br>
17757  * Create a data model bound view:
17758  <pre><code>
17759  var store = new Roo.data.Store(...);
17760
17761  var view = new Roo.View({
17762     el : "my-element",
17763     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
17764  
17765     singleSelect: true,
17766     selectedClass: "ydataview-selected",
17767     store: store
17768  });
17769
17770  // listen for node click?
17771  view.on("click", function(vw, index, node, e){
17772  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17773  });
17774
17775  // load XML data
17776  dataModel.load("foobar.xml");
17777  </code></pre>
17778  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17779  * <br><br>
17780  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17781  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17782  * 
17783  * Note: old style constructor is still suported (container, template, config)
17784  * 
17785  * @constructor
17786  * Create a new View
17787  * @param {Object} config The config object
17788  * 
17789  */
17790 Roo.View = function(config, depreciated_tpl, depreciated_config){
17791     
17792     this.parent = false;
17793     
17794     if (typeof(depreciated_tpl) == 'undefined') {
17795         // new way.. - universal constructor.
17796         Roo.apply(this, config);
17797         this.el  = Roo.get(this.el);
17798     } else {
17799         // old format..
17800         this.el  = Roo.get(config);
17801         this.tpl = depreciated_tpl;
17802         Roo.apply(this, depreciated_config);
17803     }
17804     this.wrapEl  = this.el.wrap().wrap();
17805     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17806     
17807     
17808     if(typeof(this.tpl) == "string"){
17809         this.tpl = new Roo.Template(this.tpl);
17810     } else {
17811         // support xtype ctors..
17812         this.tpl = new Roo.factory(this.tpl, Roo);
17813     }
17814     
17815     
17816     this.tpl.compile();
17817     
17818     /** @private */
17819     this.addEvents({
17820         /**
17821          * @event beforeclick
17822          * Fires before a click is processed. Returns false to cancel the default action.
17823          * @param {Roo.View} this
17824          * @param {Number} index The index of the target node
17825          * @param {HTMLElement} node The target node
17826          * @param {Roo.EventObject} e The raw event object
17827          */
17828             "beforeclick" : true,
17829         /**
17830          * @event click
17831          * Fires when a template node is clicked.
17832          * @param {Roo.View} this
17833          * @param {Number} index The index of the target node
17834          * @param {HTMLElement} node The target node
17835          * @param {Roo.EventObject} e The raw event object
17836          */
17837             "click" : true,
17838         /**
17839          * @event dblclick
17840          * Fires when a template node is double clicked.
17841          * @param {Roo.View} this
17842          * @param {Number} index The index of the target node
17843          * @param {HTMLElement} node The target node
17844          * @param {Roo.EventObject} e The raw event object
17845          */
17846             "dblclick" : true,
17847         /**
17848          * @event contextmenu
17849          * Fires when a template node is right clicked.
17850          * @param {Roo.View} this
17851          * @param {Number} index The index of the target node
17852          * @param {HTMLElement} node The target node
17853          * @param {Roo.EventObject} e The raw event object
17854          */
17855             "contextmenu" : true,
17856         /**
17857          * @event selectionchange
17858          * Fires when the selected nodes change.
17859          * @param {Roo.View} this
17860          * @param {Array} selections Array of the selected nodes
17861          */
17862             "selectionchange" : true,
17863     
17864         /**
17865          * @event beforeselect
17866          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17867          * @param {Roo.View} this
17868          * @param {HTMLElement} node The node to be selected
17869          * @param {Array} selections Array of currently selected nodes
17870          */
17871             "beforeselect" : true,
17872         /**
17873          * @event preparedata
17874          * Fires on every row to render, to allow you to change the data.
17875          * @param {Roo.View} this
17876          * @param {Object} data to be rendered (change this)
17877          */
17878           "preparedata" : true
17879           
17880           
17881         });
17882
17883
17884
17885     this.el.on({
17886         "click": this.onClick,
17887         "dblclick": this.onDblClick,
17888         "contextmenu": this.onContextMenu,
17889         scope:this
17890     });
17891
17892     this.selections = [];
17893     this.nodes = [];
17894     this.cmp = new Roo.CompositeElementLite([]);
17895     if(this.store){
17896         this.store = Roo.factory(this.store, Roo.data);
17897         this.setStore(this.store, true);
17898     }
17899     
17900     if ( this.footer && this.footer.xtype) {
17901            
17902          var fctr = this.wrapEl.appendChild(document.createElement("div"));
17903         
17904         this.footer.dataSource = this.store;
17905         this.footer.container = fctr;
17906         this.footer = Roo.factory(this.footer, Roo);
17907         fctr.insertFirst(this.el);
17908         
17909         // this is a bit insane - as the paging toolbar seems to detach the el..
17910 //        dom.parentNode.parentNode.parentNode
17911          // they get detached?
17912     }
17913     
17914     
17915     Roo.View.superclass.constructor.call(this);
17916     
17917     
17918 };
17919
17920 Roo.extend(Roo.View, Roo.util.Observable, {
17921     
17922      /**
17923      * @cfg {Roo.data.Store} store Data store to load data from.
17924      */
17925     store : false,
17926     
17927     /**
17928      * @cfg {String|Roo.Element} el The container element.
17929      */
17930     el : '',
17931     
17932     /**
17933      * @cfg {String|Roo.Template} tpl The template used by this View 
17934      */
17935     tpl : false,
17936     /**
17937      * @cfg {String} dataName the named area of the template to use as the data area
17938      *                          Works with domtemplates roo-name="name"
17939      */
17940     dataName: false,
17941     /**
17942      * @cfg {String} selectedClass The css class to add to selected nodes
17943      */
17944     selectedClass : "x-view-selected",
17945      /**
17946      * @cfg {String} emptyText The empty text to show when nothing is loaded.
17947      */
17948     emptyText : "",
17949     
17950     /**
17951      * @cfg {String} text to display on mask (default Loading)
17952      */
17953     mask : false,
17954     /**
17955      * @cfg {Boolean} multiSelect Allow multiple selection
17956      */
17957     multiSelect : false,
17958     /**
17959      * @cfg {Boolean} singleSelect Allow single selection
17960      */
17961     singleSelect:  false,
17962     
17963     /**
17964      * @cfg {Boolean} toggleSelect - selecting 
17965      */
17966     toggleSelect : false,
17967     
17968     /**
17969      * @cfg {Boolean} tickable - selecting 
17970      */
17971     tickable : false,
17972     
17973     /**
17974      * Returns the element this view is bound to.
17975      * @return {Roo.Element}
17976      */
17977     getEl : function(){
17978         return this.wrapEl;
17979     },
17980     
17981     
17982
17983     /**
17984      * Refreshes the view. - called by datachanged on the store. - do not call directly.
17985      */
17986     refresh : function(){
17987         //Roo.log('refresh');
17988         var t = this.tpl;
17989         
17990         // if we are using something like 'domtemplate', then
17991         // the what gets used is:
17992         // t.applySubtemplate(NAME, data, wrapping data..)
17993         // the outer template then get' applied with
17994         //     the store 'extra data'
17995         // and the body get's added to the
17996         //      roo-name="data" node?
17997         //      <span class='roo-tpl-{name}'></span> ?????
17998         
17999         
18000         
18001         this.clearSelections();
18002         this.el.update("");
18003         var html = [];
18004         var records = this.store.getRange();
18005         if(records.length < 1) {
18006             
18007             // is this valid??  = should it render a template??
18008             
18009             this.el.update(this.emptyText);
18010             return;
18011         }
18012         var el = this.el;
18013         if (this.dataName) {
18014             this.el.update(t.apply(this.store.meta)); //????
18015             el = this.el.child('.roo-tpl-' + this.dataName);
18016         }
18017         
18018         for(var i = 0, len = records.length; i < len; i++){
18019             var data = this.prepareData(records[i].data, i, records[i]);
18020             this.fireEvent("preparedata", this, data, i, records[i]);
18021             
18022             var d = Roo.apply({}, data);
18023             
18024             if(this.tickable){
18025                 Roo.apply(d, {'roo-id' : Roo.id()});
18026                 
18027                 var _this = this;
18028             
18029                 Roo.each(this.parent.item, function(item){
18030                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18031                         return;
18032                     }
18033                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18034                 });
18035             }
18036             
18037             html[html.length] = Roo.util.Format.trim(
18038                 this.dataName ?
18039                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18040                     t.apply(d)
18041             );
18042         }
18043         
18044         
18045         
18046         el.update(html.join(""));
18047         this.nodes = el.dom.childNodes;
18048         this.updateIndexes(0);
18049     },
18050     
18051
18052     /**
18053      * Function to override to reformat the data that is sent to
18054      * the template for each node.
18055      * DEPRICATED - use the preparedata event handler.
18056      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18057      * a JSON object for an UpdateManager bound view).
18058      */
18059     prepareData : function(data, index, record)
18060     {
18061         this.fireEvent("preparedata", this, data, index, record);
18062         return data;
18063     },
18064
18065     onUpdate : function(ds, record){
18066         // Roo.log('on update');   
18067         this.clearSelections();
18068         var index = this.store.indexOf(record);
18069         var n = this.nodes[index];
18070         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18071         n.parentNode.removeChild(n);
18072         this.updateIndexes(index, index);
18073     },
18074
18075     
18076     
18077 // --------- FIXME     
18078     onAdd : function(ds, records, index)
18079     {
18080         //Roo.log(['on Add', ds, records, index] );        
18081         this.clearSelections();
18082         if(this.nodes.length == 0){
18083             this.refresh();
18084             return;
18085         }
18086         var n = this.nodes[index];
18087         for(var i = 0, len = records.length; i < len; i++){
18088             var d = this.prepareData(records[i].data, i, records[i]);
18089             if(n){
18090                 this.tpl.insertBefore(n, d);
18091             }else{
18092                 
18093                 this.tpl.append(this.el, d);
18094             }
18095         }
18096         this.updateIndexes(index);
18097     },
18098
18099     onRemove : function(ds, record, index){
18100        // Roo.log('onRemove');
18101         this.clearSelections();
18102         var el = this.dataName  ?
18103             this.el.child('.roo-tpl-' + this.dataName) :
18104             this.el; 
18105         
18106         el.dom.removeChild(this.nodes[index]);
18107         this.updateIndexes(index);
18108     },
18109
18110     /**
18111      * Refresh an individual node.
18112      * @param {Number} index
18113      */
18114     refreshNode : function(index){
18115         this.onUpdate(this.store, this.store.getAt(index));
18116     },
18117
18118     updateIndexes : function(startIndex, endIndex){
18119         var ns = this.nodes;
18120         startIndex = startIndex || 0;
18121         endIndex = endIndex || ns.length - 1;
18122         for(var i = startIndex; i <= endIndex; i++){
18123             ns[i].nodeIndex = i;
18124         }
18125     },
18126
18127     /**
18128      * Changes the data store this view uses and refresh the view.
18129      * @param {Store} store
18130      */
18131     setStore : function(store, initial){
18132         if(!initial && this.store){
18133             this.store.un("datachanged", this.refresh);
18134             this.store.un("add", this.onAdd);
18135             this.store.un("remove", this.onRemove);
18136             this.store.un("update", this.onUpdate);
18137             this.store.un("clear", this.refresh);
18138             this.store.un("beforeload", this.onBeforeLoad);
18139             this.store.un("load", this.onLoad);
18140             this.store.un("loadexception", this.onLoad);
18141         }
18142         if(store){
18143           
18144             store.on("datachanged", this.refresh, this);
18145             store.on("add", this.onAdd, this);
18146             store.on("remove", this.onRemove, this);
18147             store.on("update", this.onUpdate, this);
18148             store.on("clear", this.refresh, this);
18149             store.on("beforeload", this.onBeforeLoad, this);
18150             store.on("load", this.onLoad, this);
18151             store.on("loadexception", this.onLoad, this);
18152         }
18153         
18154         if(store){
18155             this.refresh();
18156         }
18157     },
18158     /**
18159      * onbeforeLoad - masks the loading area.
18160      *
18161      */
18162     onBeforeLoad : function(store,opts)
18163     {
18164          //Roo.log('onBeforeLoad');   
18165         if (!opts.add) {
18166             this.el.update("");
18167         }
18168         this.el.mask(this.mask ? this.mask : "Loading" ); 
18169     },
18170     onLoad : function ()
18171     {
18172         this.el.unmask();
18173     },
18174     
18175
18176     /**
18177      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18178      * @param {HTMLElement} node
18179      * @return {HTMLElement} The template node
18180      */
18181     findItemFromChild : function(node){
18182         var el = this.dataName  ?
18183             this.el.child('.roo-tpl-' + this.dataName,true) :
18184             this.el.dom; 
18185         
18186         if(!node || node.parentNode == el){
18187                     return node;
18188             }
18189             var p = node.parentNode;
18190             while(p && p != el){
18191             if(p.parentNode == el){
18192                 return p;
18193             }
18194             p = p.parentNode;
18195         }
18196             return null;
18197     },
18198
18199     /** @ignore */
18200     onClick : function(e){
18201         var item = this.findItemFromChild(e.getTarget());
18202         if(item){
18203             var index = this.indexOf(item);
18204             if(this.onItemClick(item, index, e) !== false){
18205                 this.fireEvent("click", this, index, item, e);
18206             }
18207         }else{
18208             this.clearSelections();
18209         }
18210     },
18211
18212     /** @ignore */
18213     onContextMenu : function(e){
18214         var item = this.findItemFromChild(e.getTarget());
18215         if(item){
18216             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18217         }
18218     },
18219
18220     /** @ignore */
18221     onDblClick : function(e){
18222         var item = this.findItemFromChild(e.getTarget());
18223         if(item){
18224             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18225         }
18226     },
18227
18228     onItemClick : function(item, index, e)
18229     {
18230         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18231             return false;
18232         }
18233         if (this.toggleSelect) {
18234             var m = this.isSelected(item) ? 'unselect' : 'select';
18235             //Roo.log(m);
18236             var _t = this;
18237             _t[m](item, true, false);
18238             return true;
18239         }
18240         if(this.multiSelect || this.singleSelect){
18241             if(this.multiSelect && e.shiftKey && this.lastSelection){
18242                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18243             }else{
18244                 this.select(item, this.multiSelect && e.ctrlKey);
18245                 this.lastSelection = item;
18246             }
18247             
18248             if(!this.tickable){
18249                 e.preventDefault();
18250             }
18251             
18252         }
18253         return true;
18254     },
18255
18256     /**
18257      * Get the number of selected nodes.
18258      * @return {Number}
18259      */
18260     getSelectionCount : function(){
18261         return this.selections.length;
18262     },
18263
18264     /**
18265      * Get the currently selected nodes.
18266      * @return {Array} An array of HTMLElements
18267      */
18268     getSelectedNodes : function(){
18269         return this.selections;
18270     },
18271
18272     /**
18273      * Get the indexes of the selected nodes.
18274      * @return {Array}
18275      */
18276     getSelectedIndexes : function(){
18277         var indexes = [], s = this.selections;
18278         for(var i = 0, len = s.length; i < len; i++){
18279             indexes.push(s[i].nodeIndex);
18280         }
18281         return indexes;
18282     },
18283
18284     /**
18285      * Clear all selections
18286      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18287      */
18288     clearSelections : function(suppressEvent){
18289         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18290             this.cmp.elements = this.selections;
18291             this.cmp.removeClass(this.selectedClass);
18292             this.selections = [];
18293             if(!suppressEvent){
18294                 this.fireEvent("selectionchange", this, this.selections);
18295             }
18296         }
18297     },
18298
18299     /**
18300      * Returns true if the passed node is selected
18301      * @param {HTMLElement/Number} node The node or node index
18302      * @return {Boolean}
18303      */
18304     isSelected : function(node){
18305         var s = this.selections;
18306         if(s.length < 1){
18307             return false;
18308         }
18309         node = this.getNode(node);
18310         return s.indexOf(node) !== -1;
18311     },
18312
18313     /**
18314      * Selects nodes.
18315      * @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
18316      * @param {Boolean} keepExisting (optional) true to keep existing selections
18317      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18318      */
18319     select : function(nodeInfo, keepExisting, suppressEvent){
18320         if(nodeInfo instanceof Array){
18321             if(!keepExisting){
18322                 this.clearSelections(true);
18323             }
18324             for(var i = 0, len = nodeInfo.length; i < len; i++){
18325                 this.select(nodeInfo[i], true, true);
18326             }
18327             return;
18328         } 
18329         var node = this.getNode(nodeInfo);
18330         if(!node || this.isSelected(node)){
18331             return; // already selected.
18332         }
18333         if(!keepExisting){
18334             this.clearSelections(true);
18335         }
18336         
18337         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18338             Roo.fly(node).addClass(this.selectedClass);
18339             this.selections.push(node);
18340             if(!suppressEvent){
18341                 this.fireEvent("selectionchange", this, this.selections);
18342             }
18343         }
18344         
18345         
18346     },
18347       /**
18348      * Unselects nodes.
18349      * @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
18350      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18351      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18352      */
18353     unselect : function(nodeInfo, keepExisting, suppressEvent)
18354     {
18355         if(nodeInfo instanceof Array){
18356             Roo.each(this.selections, function(s) {
18357                 this.unselect(s, nodeInfo);
18358             }, this);
18359             return;
18360         }
18361         var node = this.getNode(nodeInfo);
18362         if(!node || !this.isSelected(node)){
18363             //Roo.log("not selected");
18364             return; // not selected.
18365         }
18366         // fireevent???
18367         var ns = [];
18368         Roo.each(this.selections, function(s) {
18369             if (s == node ) {
18370                 Roo.fly(node).removeClass(this.selectedClass);
18371
18372                 return;
18373             }
18374             ns.push(s);
18375         },this);
18376         
18377         this.selections= ns;
18378         this.fireEvent("selectionchange", this, this.selections);
18379     },
18380
18381     /**
18382      * Gets a template node.
18383      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18384      * @return {HTMLElement} The node or null if it wasn't found
18385      */
18386     getNode : function(nodeInfo){
18387         if(typeof nodeInfo == "string"){
18388             return document.getElementById(nodeInfo);
18389         }else if(typeof nodeInfo == "number"){
18390             return this.nodes[nodeInfo];
18391         }
18392         return nodeInfo;
18393     },
18394
18395     /**
18396      * Gets a range template nodes.
18397      * @param {Number} startIndex
18398      * @param {Number} endIndex
18399      * @return {Array} An array of nodes
18400      */
18401     getNodes : function(start, end){
18402         var ns = this.nodes;
18403         start = start || 0;
18404         end = typeof end == "undefined" ? ns.length - 1 : end;
18405         var nodes = [];
18406         if(start <= end){
18407             for(var i = start; i <= end; i++){
18408                 nodes.push(ns[i]);
18409             }
18410         } else{
18411             for(var i = start; i >= end; i--){
18412                 nodes.push(ns[i]);
18413             }
18414         }
18415         return nodes;
18416     },
18417
18418     /**
18419      * Finds the index of the passed node
18420      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18421      * @return {Number} The index of the node or -1
18422      */
18423     indexOf : function(node){
18424         node = this.getNode(node);
18425         if(typeof node.nodeIndex == "number"){
18426             return node.nodeIndex;
18427         }
18428         var ns = this.nodes;
18429         for(var i = 0, len = ns.length; i < len; i++){
18430             if(ns[i] == node){
18431                 return i;
18432             }
18433         }
18434         return -1;
18435     }
18436 });
18437 /*
18438  * - LGPL
18439  *
18440  * based on jquery fullcalendar
18441  * 
18442  */
18443
18444 Roo.bootstrap = Roo.bootstrap || {};
18445 /**
18446  * @class Roo.bootstrap.Calendar
18447  * @extends Roo.bootstrap.Component
18448  * Bootstrap Calendar class
18449  * @cfg {Boolean} loadMask (true|false) default false
18450  * @cfg {Object} header generate the user specific header of the calendar, default false
18451
18452  * @constructor
18453  * Create a new Container
18454  * @param {Object} config The config object
18455  */
18456
18457
18458
18459 Roo.bootstrap.Calendar = function(config){
18460     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18461      this.addEvents({
18462         /**
18463              * @event select
18464              * Fires when a date is selected
18465              * @param {DatePicker} this
18466              * @param {Date} date The selected date
18467              */
18468         'select': true,
18469         /**
18470              * @event monthchange
18471              * Fires when the displayed month changes 
18472              * @param {DatePicker} this
18473              * @param {Date} date The selected month
18474              */
18475         'monthchange': true,
18476         /**
18477              * @event evententer
18478              * Fires when mouse over an event
18479              * @param {Calendar} this
18480              * @param {event} Event
18481              */
18482         'evententer': true,
18483         /**
18484              * @event eventleave
18485              * Fires when the mouse leaves an
18486              * @param {Calendar} this
18487              * @param {event}
18488              */
18489         'eventleave': true,
18490         /**
18491              * @event eventclick
18492              * Fires when the mouse click an
18493              * @param {Calendar} this
18494              * @param {event}
18495              */
18496         'eventclick': true
18497         
18498     });
18499
18500 };
18501
18502 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18503     
18504      /**
18505      * @cfg {Number} startDay
18506      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18507      */
18508     startDay : 0,
18509     
18510     loadMask : false,
18511     
18512     header : false,
18513       
18514     getAutoCreate : function(){
18515         
18516         
18517         var fc_button = function(name, corner, style, content ) {
18518             return Roo.apply({},{
18519                 tag : 'span',
18520                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18521                          (corner.length ?
18522                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18523                             ''
18524                         ),
18525                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18526                 unselectable: 'on'
18527             });
18528         };
18529         
18530         var header = {};
18531         
18532         if(!this.header){
18533             header = {
18534                 tag : 'table',
18535                 cls : 'fc-header',
18536                 style : 'width:100%',
18537                 cn : [
18538                     {
18539                         tag: 'tr',
18540                         cn : [
18541                             {
18542                                 tag : 'td',
18543                                 cls : 'fc-header-left',
18544                                 cn : [
18545                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18546                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18547                                     { tag: 'span', cls: 'fc-header-space' },
18548                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18549
18550
18551                                 ]
18552                             },
18553
18554                             {
18555                                 tag : 'td',
18556                                 cls : 'fc-header-center',
18557                                 cn : [
18558                                     {
18559                                         tag: 'span',
18560                                         cls: 'fc-header-title',
18561                                         cn : {
18562                                             tag: 'H2',
18563                                             html : 'month / year'
18564                                         }
18565                                     }
18566
18567                                 ]
18568                             },
18569                             {
18570                                 tag : 'td',
18571                                 cls : 'fc-header-right',
18572                                 cn : [
18573                               /*      fc_button('month', 'left', '', 'month' ),
18574                                     fc_button('week', '', '', 'week' ),
18575                                     fc_button('day', 'right', '', 'day' )
18576                                 */    
18577
18578                                 ]
18579                             }
18580
18581                         ]
18582                     }
18583                 ]
18584             };
18585         }
18586         
18587         header = this.header;
18588         
18589        
18590         var cal_heads = function() {
18591             var ret = [];
18592             // fixme - handle this.
18593             
18594             for (var i =0; i < Date.dayNames.length; i++) {
18595                 var d = Date.dayNames[i];
18596                 ret.push({
18597                     tag: 'th',
18598                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18599                     html : d.substring(0,3)
18600                 });
18601                 
18602             }
18603             ret[0].cls += ' fc-first';
18604             ret[6].cls += ' fc-last';
18605             return ret;
18606         };
18607         var cal_cell = function(n) {
18608             return  {
18609                 tag: 'td',
18610                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18611                 cn : [
18612                     {
18613                         cn : [
18614                             {
18615                                 cls: 'fc-day-number',
18616                                 html: 'D'
18617                             },
18618                             {
18619                                 cls: 'fc-day-content',
18620                              
18621                                 cn : [
18622                                      {
18623                                         style: 'position: relative;' // height: 17px;
18624                                     }
18625                                 ]
18626                             }
18627                             
18628                             
18629                         ]
18630                     }
18631                 ]
18632                 
18633             }
18634         };
18635         var cal_rows = function() {
18636             
18637             var ret = [];
18638             for (var r = 0; r < 6; r++) {
18639                 var row= {
18640                     tag : 'tr',
18641                     cls : 'fc-week',
18642                     cn : []
18643                 };
18644                 
18645                 for (var i =0; i < Date.dayNames.length; i++) {
18646                     var d = Date.dayNames[i];
18647                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18648
18649                 }
18650                 row.cn[0].cls+=' fc-first';
18651                 row.cn[0].cn[0].style = 'min-height:90px';
18652                 row.cn[6].cls+=' fc-last';
18653                 ret.push(row);
18654                 
18655             }
18656             ret[0].cls += ' fc-first';
18657             ret[4].cls += ' fc-prev-last';
18658             ret[5].cls += ' fc-last';
18659             return ret;
18660             
18661         };
18662         
18663         var cal_table = {
18664             tag: 'table',
18665             cls: 'fc-border-separate',
18666             style : 'width:100%',
18667             cellspacing  : 0,
18668             cn : [
18669                 { 
18670                     tag: 'thead',
18671                     cn : [
18672                         { 
18673                             tag: 'tr',
18674                             cls : 'fc-first fc-last',
18675                             cn : cal_heads()
18676                         }
18677                     ]
18678                 },
18679                 { 
18680                     tag: 'tbody',
18681                     cn : cal_rows()
18682                 }
18683                   
18684             ]
18685         };
18686          
18687          var cfg = {
18688             cls : 'fc fc-ltr',
18689             cn : [
18690                 header,
18691                 {
18692                     cls : 'fc-content',
18693                     style : "position: relative;",
18694                     cn : [
18695                         {
18696                             cls : 'fc-view fc-view-month fc-grid',
18697                             style : 'position: relative',
18698                             unselectable : 'on',
18699                             cn : [
18700                                 {
18701                                     cls : 'fc-event-container',
18702                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18703                                 },
18704                                 cal_table
18705                             ]
18706                         }
18707                     ]
18708     
18709                 }
18710            ] 
18711             
18712         };
18713         
18714          
18715         
18716         return cfg;
18717     },
18718     
18719     
18720     initEvents : function()
18721     {
18722         if(!this.store){
18723             throw "can not find store for calendar";
18724         }
18725         
18726         var mark = {
18727             tag: "div",
18728             cls:"x-dlg-mask",
18729             style: "text-align:center",
18730             cn: [
18731                 {
18732                     tag: "div",
18733                     style: "background-color:white;width:50%;margin:250 auto",
18734                     cn: [
18735                         {
18736                             tag: "img",
18737                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18738                         },
18739                         {
18740                             tag: "span",
18741                             html: "Loading"
18742                         }
18743                         
18744                     ]
18745                 }
18746             ]
18747         };
18748         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18749         
18750         var size = this.el.select('.fc-content', true).first().getSize();
18751         this.maskEl.setSize(size.width, size.height);
18752         this.maskEl.enableDisplayMode("block");
18753         if(!this.loadMask){
18754             this.maskEl.hide();
18755         }
18756         
18757         this.store = Roo.factory(this.store, Roo.data);
18758         this.store.on('load', this.onLoad, this);
18759         this.store.on('beforeload', this.onBeforeLoad, this);
18760         
18761         this.resize();
18762         
18763         this.cells = this.el.select('.fc-day',true);
18764         //Roo.log(this.cells);
18765         this.textNodes = this.el.query('.fc-day-number');
18766         this.cells.addClassOnOver('fc-state-hover');
18767         
18768         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18769         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18770         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18771         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18772         
18773         this.on('monthchange', this.onMonthChange, this);
18774         
18775         this.update(new Date().clearTime());
18776     },
18777     
18778     resize : function() {
18779         var sz  = this.el.getSize();
18780         
18781         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18782         this.el.select('.fc-day-content div',true).setHeight(34);
18783     },
18784     
18785     
18786     // private
18787     showPrevMonth : function(e){
18788         this.update(this.activeDate.add("mo", -1));
18789     },
18790     showToday : function(e){
18791         this.update(new Date().clearTime());
18792     },
18793     // private
18794     showNextMonth : function(e){
18795         this.update(this.activeDate.add("mo", 1));
18796     },
18797
18798     // private
18799     showPrevYear : function(){
18800         this.update(this.activeDate.add("y", -1));
18801     },
18802
18803     // private
18804     showNextYear : function(){
18805         this.update(this.activeDate.add("y", 1));
18806     },
18807
18808     
18809    // private
18810     update : function(date)
18811     {
18812         var vd = this.activeDate;
18813         this.activeDate = date;
18814 //        if(vd && this.el){
18815 //            var t = date.getTime();
18816 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18817 //                Roo.log('using add remove');
18818 //                
18819 //                this.fireEvent('monthchange', this, date);
18820 //                
18821 //                this.cells.removeClass("fc-state-highlight");
18822 //                this.cells.each(function(c){
18823 //                   if(c.dateValue == t){
18824 //                       c.addClass("fc-state-highlight");
18825 //                       setTimeout(function(){
18826 //                            try{c.dom.firstChild.focus();}catch(e){}
18827 //                       }, 50);
18828 //                       return false;
18829 //                   }
18830 //                   return true;
18831 //                });
18832 //                return;
18833 //            }
18834 //        }
18835         
18836         var days = date.getDaysInMonth();
18837         
18838         var firstOfMonth = date.getFirstDateOfMonth();
18839         var startingPos = firstOfMonth.getDay()-this.startDay;
18840         
18841         if(startingPos < this.startDay){
18842             startingPos += 7;
18843         }
18844         
18845         var pm = date.add(Date.MONTH, -1);
18846         var prevStart = pm.getDaysInMonth()-startingPos;
18847 //        
18848         this.cells = this.el.select('.fc-day',true);
18849         this.textNodes = this.el.query('.fc-day-number');
18850         this.cells.addClassOnOver('fc-state-hover');
18851         
18852         var cells = this.cells.elements;
18853         var textEls = this.textNodes;
18854         
18855         Roo.each(cells, function(cell){
18856             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18857         });
18858         
18859         days += startingPos;
18860
18861         // convert everything to numbers so it's fast
18862         var day = 86400000;
18863         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18864         //Roo.log(d);
18865         //Roo.log(pm);
18866         //Roo.log(prevStart);
18867         
18868         var today = new Date().clearTime().getTime();
18869         var sel = date.clearTime().getTime();
18870         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18871         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18872         var ddMatch = this.disabledDatesRE;
18873         var ddText = this.disabledDatesText;
18874         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18875         var ddaysText = this.disabledDaysText;
18876         var format = this.format;
18877         
18878         var setCellClass = function(cal, cell){
18879             cell.row = 0;
18880             cell.events = [];
18881             cell.more = [];
18882             //Roo.log('set Cell Class');
18883             cell.title = "";
18884             var t = d.getTime();
18885             
18886             //Roo.log(d);
18887             
18888             cell.dateValue = t;
18889             if(t == today){
18890                 cell.className += " fc-today";
18891                 cell.className += " fc-state-highlight";
18892                 cell.title = cal.todayText;
18893             }
18894             if(t == sel){
18895                 // disable highlight in other month..
18896                 //cell.className += " fc-state-highlight";
18897                 
18898             }
18899             // disabling
18900             if(t < min) {
18901                 cell.className = " fc-state-disabled";
18902                 cell.title = cal.minText;
18903                 return;
18904             }
18905             if(t > max) {
18906                 cell.className = " fc-state-disabled";
18907                 cell.title = cal.maxText;
18908                 return;
18909             }
18910             if(ddays){
18911                 if(ddays.indexOf(d.getDay()) != -1){
18912                     cell.title = ddaysText;
18913                     cell.className = " fc-state-disabled";
18914                 }
18915             }
18916             if(ddMatch && format){
18917                 var fvalue = d.dateFormat(format);
18918                 if(ddMatch.test(fvalue)){
18919                     cell.title = ddText.replace("%0", fvalue);
18920                     cell.className = " fc-state-disabled";
18921                 }
18922             }
18923             
18924             if (!cell.initialClassName) {
18925                 cell.initialClassName = cell.dom.className;
18926             }
18927             
18928             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
18929         };
18930
18931         var i = 0;
18932         
18933         for(; i < startingPos; i++) {
18934             textEls[i].innerHTML = (++prevStart);
18935             d.setDate(d.getDate()+1);
18936             
18937             cells[i].className = "fc-past fc-other-month";
18938             setCellClass(this, cells[i]);
18939         }
18940         
18941         var intDay = 0;
18942         
18943         for(; i < days; i++){
18944             intDay = i - startingPos + 1;
18945             textEls[i].innerHTML = (intDay);
18946             d.setDate(d.getDate()+1);
18947             
18948             cells[i].className = ''; // "x-date-active";
18949             setCellClass(this, cells[i]);
18950         }
18951         var extraDays = 0;
18952         
18953         for(; i < 42; i++) {
18954             textEls[i].innerHTML = (++extraDays);
18955             d.setDate(d.getDate()+1);
18956             
18957             cells[i].className = "fc-future fc-other-month";
18958             setCellClass(this, cells[i]);
18959         }
18960         
18961         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
18962         
18963         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
18964         
18965         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
18966         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
18967         
18968         if(totalRows != 6){
18969             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
18970             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
18971         }
18972         
18973         this.fireEvent('monthchange', this, date);
18974         
18975         
18976         /*
18977         if(!this.internalRender){
18978             var main = this.el.dom.firstChild;
18979             var w = main.offsetWidth;
18980             this.el.setWidth(w + this.el.getBorderWidth("lr"));
18981             Roo.fly(main).setWidth(w);
18982             this.internalRender = true;
18983             // opera does not respect the auto grow header center column
18984             // then, after it gets a width opera refuses to recalculate
18985             // without a second pass
18986             if(Roo.isOpera && !this.secondPass){
18987                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
18988                 this.secondPass = true;
18989                 this.update.defer(10, this, [date]);
18990             }
18991         }
18992         */
18993         
18994     },
18995     
18996     findCell : function(dt) {
18997         dt = dt.clearTime().getTime();
18998         var ret = false;
18999         this.cells.each(function(c){
19000             //Roo.log("check " +c.dateValue + '?=' + dt);
19001             if(c.dateValue == dt){
19002                 ret = c;
19003                 return false;
19004             }
19005             return true;
19006         });
19007         
19008         return ret;
19009     },
19010     
19011     findCells : function(ev) {
19012         var s = ev.start.clone().clearTime().getTime();
19013        // Roo.log(s);
19014         var e= ev.end.clone().clearTime().getTime();
19015        // Roo.log(e);
19016         var ret = [];
19017         this.cells.each(function(c){
19018              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19019             
19020             if(c.dateValue > e){
19021                 return ;
19022             }
19023             if(c.dateValue < s){
19024                 return ;
19025             }
19026             ret.push(c);
19027         });
19028         
19029         return ret;    
19030     },
19031     
19032 //    findBestRow: function(cells)
19033 //    {
19034 //        var ret = 0;
19035 //        
19036 //        for (var i =0 ; i < cells.length;i++) {
19037 //            ret  = Math.max(cells[i].rows || 0,ret);
19038 //        }
19039 //        return ret;
19040 //        
19041 //    },
19042     
19043     
19044     addItem : function(ev)
19045     {
19046         // look for vertical location slot in
19047         var cells = this.findCells(ev);
19048         
19049 //        ev.row = this.findBestRow(cells);
19050         
19051         // work out the location.
19052         
19053         var crow = false;
19054         var rows = [];
19055         for(var i =0; i < cells.length; i++) {
19056             
19057             cells[i].row = cells[0].row;
19058             
19059             if(i == 0){
19060                 cells[i].row = cells[i].row + 1;
19061             }
19062             
19063             if (!crow) {
19064                 crow = {
19065                     start : cells[i],
19066                     end :  cells[i]
19067                 };
19068                 continue;
19069             }
19070             if (crow.start.getY() == cells[i].getY()) {
19071                 // on same row.
19072                 crow.end = cells[i];
19073                 continue;
19074             }
19075             // different row.
19076             rows.push(crow);
19077             crow = {
19078                 start: cells[i],
19079                 end : cells[i]
19080             };
19081             
19082         }
19083         
19084         rows.push(crow);
19085         ev.els = [];
19086         ev.rows = rows;
19087         ev.cells = cells;
19088         
19089         cells[0].events.push(ev);
19090         
19091         this.calevents.push(ev);
19092     },
19093     
19094     clearEvents: function() {
19095         
19096         if(!this.calevents){
19097             return;
19098         }
19099         
19100         Roo.each(this.cells.elements, function(c){
19101             c.row = 0;
19102             c.events = [];
19103             c.more = [];
19104         });
19105         
19106         Roo.each(this.calevents, function(e) {
19107             Roo.each(e.els, function(el) {
19108                 el.un('mouseenter' ,this.onEventEnter, this);
19109                 el.un('mouseleave' ,this.onEventLeave, this);
19110                 el.remove();
19111             },this);
19112         },this);
19113         
19114         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19115             e.remove();
19116         });
19117         
19118     },
19119     
19120     renderEvents: function()
19121     {   
19122         var _this = this;
19123         
19124         this.cells.each(function(c) {
19125             
19126             if(c.row < 5){
19127                 return;
19128             }
19129             
19130             var ev = c.events;
19131             
19132             var r = 4;
19133             if(c.row != c.events.length){
19134                 r = 4 - (4 - (c.row - c.events.length));
19135             }
19136             
19137             c.events = ev.slice(0, r);
19138             c.more = ev.slice(r);
19139             
19140             if(c.more.length && c.more.length == 1){
19141                 c.events.push(c.more.pop());
19142             }
19143             
19144             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19145             
19146         });
19147             
19148         this.cells.each(function(c) {
19149             
19150             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19151             
19152             
19153             for (var e = 0; e < c.events.length; e++){
19154                 var ev = c.events[e];
19155                 var rows = ev.rows;
19156                 
19157                 for(var i = 0; i < rows.length; i++) {
19158                 
19159                     // how many rows should it span..
19160
19161                     var  cfg = {
19162                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19163                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19164
19165                         unselectable : "on",
19166                         cn : [
19167                             {
19168                                 cls: 'fc-event-inner',
19169                                 cn : [
19170     //                                {
19171     //                                  tag:'span',
19172     //                                  cls: 'fc-event-time',
19173     //                                  html : cells.length > 1 ? '' : ev.time
19174     //                                },
19175                                     {
19176                                       tag:'span',
19177                                       cls: 'fc-event-title',
19178                                       html : String.format('{0}', ev.title)
19179                                     }
19180
19181
19182                                 ]
19183                             },
19184                             {
19185                                 cls: 'ui-resizable-handle ui-resizable-e',
19186                                 html : '&nbsp;&nbsp;&nbsp'
19187                             }
19188
19189                         ]
19190                     };
19191
19192                     if (i == 0) {
19193                         cfg.cls += ' fc-event-start';
19194                     }
19195                     if ((i+1) == rows.length) {
19196                         cfg.cls += ' fc-event-end';
19197                     }
19198
19199                     var ctr = _this.el.select('.fc-event-container',true).first();
19200                     var cg = ctr.createChild(cfg);
19201
19202                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19203                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19204
19205                     var r = (c.more.length) ? 1 : 0;
19206                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19207                     cg.setWidth(ebox.right - sbox.x -2);
19208
19209                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19210                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19211                     cg.on('click', _this.onEventClick, _this, ev);
19212
19213                     ev.els.push(cg);
19214                     
19215                 }
19216                 
19217             }
19218             
19219             
19220             if(c.more.length){
19221                 var  cfg = {
19222                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19223                     style : 'position: absolute',
19224                     unselectable : "on",
19225                     cn : [
19226                         {
19227                             cls: 'fc-event-inner',
19228                             cn : [
19229                                 {
19230                                   tag:'span',
19231                                   cls: 'fc-event-title',
19232                                   html : 'More'
19233                                 }
19234
19235
19236                             ]
19237                         },
19238                         {
19239                             cls: 'ui-resizable-handle ui-resizable-e',
19240                             html : '&nbsp;&nbsp;&nbsp'
19241                         }
19242
19243                     ]
19244                 };
19245
19246                 var ctr = _this.el.select('.fc-event-container',true).first();
19247                 var cg = ctr.createChild(cfg);
19248
19249                 var sbox = c.select('.fc-day-content',true).first().getBox();
19250                 var ebox = c.select('.fc-day-content',true).first().getBox();
19251                 //Roo.log(cg);
19252                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19253                 cg.setWidth(ebox.right - sbox.x -2);
19254
19255                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19256                 
19257             }
19258             
19259         });
19260         
19261         
19262         
19263     },
19264     
19265     onEventEnter: function (e, el,event,d) {
19266         this.fireEvent('evententer', this, el, event);
19267     },
19268     
19269     onEventLeave: function (e, el,event,d) {
19270         this.fireEvent('eventleave', this, el, event);
19271     },
19272     
19273     onEventClick: function (e, el,event,d) {
19274         this.fireEvent('eventclick', this, el, event);
19275     },
19276     
19277     onMonthChange: function () {
19278         this.store.load();
19279     },
19280     
19281     onMoreEventClick: function(e, el, more)
19282     {
19283         var _this = this;
19284         
19285         this.calpopover.placement = 'right';
19286         this.calpopover.setTitle('More');
19287         
19288         this.calpopover.setContent('');
19289         
19290         var ctr = this.calpopover.el.select('.popover-content', true).first();
19291         
19292         Roo.each(more, function(m){
19293             var cfg = {
19294                 cls : 'fc-event-hori fc-event-draggable',
19295                 html : m.title
19296             };
19297             var cg = ctr.createChild(cfg);
19298             
19299             cg.on('click', _this.onEventClick, _this, m);
19300         });
19301         
19302         this.calpopover.show(el);
19303         
19304         
19305     },
19306     
19307     onLoad: function () 
19308     {   
19309         this.calevents = [];
19310         var cal = this;
19311         
19312         if(this.store.getCount() > 0){
19313             this.store.data.each(function(d){
19314                cal.addItem({
19315                     id : d.data.id,
19316                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19317                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19318                     time : d.data.start_time,
19319                     title : d.data.title,
19320                     description : d.data.description,
19321                     venue : d.data.venue
19322                 });
19323             });
19324         }
19325         
19326         this.renderEvents();
19327         
19328         if(this.calevents.length && this.loadMask){
19329             this.maskEl.hide();
19330         }
19331     },
19332     
19333     onBeforeLoad: function()
19334     {
19335         this.clearEvents();
19336         if(this.loadMask){
19337             this.maskEl.show();
19338         }
19339     }
19340 });
19341
19342  
19343  /*
19344  * - LGPL
19345  *
19346  * element
19347  * 
19348  */
19349
19350 /**
19351  * @class Roo.bootstrap.Popover
19352  * @extends Roo.bootstrap.Component
19353  * Bootstrap Popover class
19354  * @cfg {String} html contents of the popover   (or false to use children..)
19355  * @cfg {String} title of popover (or false to hide)
19356  * @cfg {String} placement how it is placed
19357  * @cfg {String} trigger click || hover (or false to trigger manually)
19358  * @cfg {String} over what (parent or false to trigger manually.)
19359  * @cfg {Number} delay - delay before showing
19360  
19361  * @constructor
19362  * Create a new Popover
19363  * @param {Object} config The config object
19364  */
19365
19366 Roo.bootstrap.Popover = function(config){
19367     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19368     
19369     this.addEvents({
19370         // raw events
19371          /**
19372          * @event show
19373          * After the popover show
19374          * 
19375          * @param {Roo.bootstrap.Popover} this
19376          */
19377         "show" : true,
19378         /**
19379          * @event hide
19380          * After the popover hide
19381          * 
19382          * @param {Roo.bootstrap.Popover} this
19383          */
19384         "hide" : true
19385     });
19386 };
19387
19388 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19389     
19390     title: 'Fill in a title',
19391     html: false,
19392     
19393     placement : 'right',
19394     trigger : 'hover', // hover
19395     
19396     delay : 0,
19397     
19398     over: 'parent',
19399     
19400     can_build_overlaid : false,
19401     
19402     getChildContainer : function()
19403     {
19404         return this.el.select('.popover-content',true).first();
19405     },
19406     
19407     getAutoCreate : function(){
19408          
19409         var cfg = {
19410            cls : 'popover roo-dynamic',
19411            style: 'display:block',
19412            cn : [
19413                 {
19414                     cls : 'arrow'
19415                 },
19416                 {
19417                     cls : 'popover-inner',
19418                     cn : [
19419                         {
19420                             tag: 'h3',
19421                             cls: 'popover-title popover-header',
19422                             html : this.title
19423                         },
19424                         {
19425                             cls : 'popover-content popover-body',
19426                             html : this.html
19427                         }
19428                     ]
19429                     
19430                 }
19431            ]
19432         };
19433         
19434         return cfg;
19435     },
19436     setTitle: function(str)
19437     {
19438         this.title = str;
19439         this.el.select('.popover-title',true).first().dom.innerHTML = str;
19440     },
19441     setContent: function(str)
19442     {
19443         this.html = str;
19444         this.el.select('.popover-content',true).first().dom.innerHTML = str;
19445     },
19446     // as it get's added to the bottom of the page.
19447     onRender : function(ct, position)
19448     {
19449         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19450         if(!this.el){
19451             var cfg = Roo.apply({},  this.getAutoCreate());
19452             cfg.id = Roo.id();
19453             
19454             if (this.cls) {
19455                 cfg.cls += ' ' + this.cls;
19456             }
19457             if (this.style) {
19458                 cfg.style = this.style;
19459             }
19460             //Roo.log("adding to ");
19461             this.el = Roo.get(document.body).createChild(cfg, position);
19462 //            Roo.log(this.el);
19463         }
19464         this.initEvents();
19465     },
19466     
19467     initEvents : function()
19468     {
19469         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19470         this.el.enableDisplayMode('block');
19471         this.el.hide();
19472         if (this.over === false) {
19473             return; 
19474         }
19475         if (this.triggers === false) {
19476             return;
19477         }
19478         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19479         var triggers = this.trigger ? this.trigger.split(' ') : [];
19480         Roo.each(triggers, function(trigger) {
19481         
19482             if (trigger == 'click') {
19483                 on_el.on('click', this.toggle, this);
19484             } else if (trigger != 'manual') {
19485                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19486                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19487       
19488                 on_el.on(eventIn  ,this.enter, this);
19489                 on_el.on(eventOut, this.leave, this);
19490             }
19491         }, this);
19492         
19493     },
19494     
19495     
19496     // private
19497     timeout : null,
19498     hoverState : null,
19499     
19500     toggle : function () {
19501         this.hoverState == 'in' ? this.leave() : this.enter();
19502     },
19503     
19504     enter : function () {
19505         
19506         clearTimeout(this.timeout);
19507     
19508         this.hoverState = 'in';
19509     
19510         if (!this.delay || !this.delay.show) {
19511             this.show();
19512             return;
19513         }
19514         var _t = this;
19515         this.timeout = setTimeout(function () {
19516             if (_t.hoverState == 'in') {
19517                 _t.show();
19518             }
19519         }, this.delay.show)
19520     },
19521     
19522     leave : function() {
19523         clearTimeout(this.timeout);
19524     
19525         this.hoverState = 'out';
19526     
19527         if (!this.delay || !this.delay.hide) {
19528             this.hide();
19529             return;
19530         }
19531         var _t = this;
19532         this.timeout = setTimeout(function () {
19533             if (_t.hoverState == 'out') {
19534                 _t.hide();
19535             }
19536         }, this.delay.hide)
19537     },
19538     
19539     show : function (on_el)
19540     {
19541         if (!on_el) {
19542             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19543         }
19544         
19545         // set content.
19546         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19547         if (this.html !== false) {
19548             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19549         }
19550         this.el.removeClass([
19551             'fade','top','bottom', 'left', 'right','in',
19552             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19553         ]);
19554         if (!this.title.length) {
19555             this.el.select('.popover-title',true).hide();
19556         }
19557         
19558         var placement = typeof this.placement == 'function' ?
19559             this.placement.call(this, this.el, on_el) :
19560             this.placement;
19561             
19562         var autoToken = /\s?auto?\s?/i;
19563         var autoPlace = autoToken.test(placement);
19564         if (autoPlace) {
19565             placement = placement.replace(autoToken, '') || 'top';
19566         }
19567         
19568         //this.el.detach()
19569         //this.el.setXY([0,0]);
19570         this.el.show();
19571         this.el.dom.style.display='block';
19572         this.el.addClass(placement);
19573         
19574         //this.el.appendTo(on_el);
19575         
19576         var p = this.getPosition();
19577         var box = this.el.getBox();
19578         
19579         if (autoPlace) {
19580             // fixme..
19581         }
19582         var align = Roo.bootstrap.Popover.alignment[placement];
19583         
19584 //        Roo.log(align);
19585         this.el.alignTo(on_el, align[0],align[1]);
19586         //var arrow = this.el.select('.arrow',true).first();
19587         //arrow.set(align[2], 
19588         
19589         this.el.addClass('in');
19590         
19591         
19592         if (this.el.hasClass('fade')) {
19593             // fade it?
19594         }
19595         
19596         this.hoverState = 'in';
19597         
19598         this.fireEvent('show', this);
19599         
19600     },
19601     hide : function()
19602     {
19603         this.el.setXY([0,0]);
19604         this.el.removeClass('in');
19605         this.el.hide();
19606         this.hoverState = null;
19607         
19608         this.fireEvent('hide', this);
19609     }
19610     
19611 });
19612
19613 Roo.bootstrap.Popover.alignment = {
19614     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19615     'right' : ['l-r', [10,0], 'left bs-popover-left'],
19616     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19617     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19618 };
19619
19620  /*
19621  * - LGPL
19622  *
19623  * Progress
19624  * 
19625  */
19626
19627 /**
19628  * @class Roo.bootstrap.Progress
19629  * @extends Roo.bootstrap.Component
19630  * Bootstrap Progress class
19631  * @cfg {Boolean} striped striped of the progress bar
19632  * @cfg {Boolean} active animated of the progress bar
19633  * 
19634  * 
19635  * @constructor
19636  * Create a new Progress
19637  * @param {Object} config The config object
19638  */
19639
19640 Roo.bootstrap.Progress = function(config){
19641     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19642 };
19643
19644 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
19645     
19646     striped : false,
19647     active: false,
19648     
19649     getAutoCreate : function(){
19650         var cfg = {
19651             tag: 'div',
19652             cls: 'progress'
19653         };
19654         
19655         
19656         if(this.striped){
19657             cfg.cls += ' progress-striped';
19658         }
19659       
19660         if(this.active){
19661             cfg.cls += ' active';
19662         }
19663         
19664         
19665         return cfg;
19666     }
19667    
19668 });
19669
19670  
19671
19672  /*
19673  * - LGPL
19674  *
19675  * ProgressBar
19676  * 
19677  */
19678
19679 /**
19680  * @class Roo.bootstrap.ProgressBar
19681  * @extends Roo.bootstrap.Component
19682  * Bootstrap ProgressBar class
19683  * @cfg {Number} aria_valuenow aria-value now
19684  * @cfg {Number} aria_valuemin aria-value min
19685  * @cfg {Number} aria_valuemax aria-value max
19686  * @cfg {String} label label for the progress bar
19687  * @cfg {String} panel (success | info | warning | danger )
19688  * @cfg {String} role role of the progress bar
19689  * @cfg {String} sr_only text
19690  * 
19691  * 
19692  * @constructor
19693  * Create a new ProgressBar
19694  * @param {Object} config The config object
19695  */
19696
19697 Roo.bootstrap.ProgressBar = function(config){
19698     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19699 };
19700
19701 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
19702     
19703     aria_valuenow : 0,
19704     aria_valuemin : 0,
19705     aria_valuemax : 100,
19706     label : false,
19707     panel : false,
19708     role : false,
19709     sr_only: false,
19710     
19711     getAutoCreate : function()
19712     {
19713         
19714         var cfg = {
19715             tag: 'div',
19716             cls: 'progress-bar',
19717             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19718         };
19719         
19720         if(this.sr_only){
19721             cfg.cn = {
19722                 tag: 'span',
19723                 cls: 'sr-only',
19724                 html: this.sr_only
19725             }
19726         }
19727         
19728         if(this.role){
19729             cfg.role = this.role;
19730         }
19731         
19732         if(this.aria_valuenow){
19733             cfg['aria-valuenow'] = this.aria_valuenow;
19734         }
19735         
19736         if(this.aria_valuemin){
19737             cfg['aria-valuemin'] = this.aria_valuemin;
19738         }
19739         
19740         if(this.aria_valuemax){
19741             cfg['aria-valuemax'] = this.aria_valuemax;
19742         }
19743         
19744         if(this.label && !this.sr_only){
19745             cfg.html = this.label;
19746         }
19747         
19748         if(this.panel){
19749             cfg.cls += ' progress-bar-' + this.panel;
19750         }
19751         
19752         return cfg;
19753     },
19754     
19755     update : function(aria_valuenow)
19756     {
19757         this.aria_valuenow = aria_valuenow;
19758         
19759         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19760     }
19761    
19762 });
19763
19764  
19765
19766  /*
19767  * - LGPL
19768  *
19769  * column
19770  * 
19771  */
19772
19773 /**
19774  * @class Roo.bootstrap.TabGroup
19775  * @extends Roo.bootstrap.Column
19776  * Bootstrap Column class
19777  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19778  * @cfg {Boolean} carousel true to make the group behave like a carousel
19779  * @cfg {Boolean} bullets show bullets for the panels
19780  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19781  * @cfg {Number} timer auto slide timer .. default 0 millisecond
19782  * @cfg {Boolean} showarrow (true|false) show arrow default true
19783  * 
19784  * @constructor
19785  * Create a new TabGroup
19786  * @param {Object} config The config object
19787  */
19788
19789 Roo.bootstrap.TabGroup = function(config){
19790     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19791     if (!this.navId) {
19792         this.navId = Roo.id();
19793     }
19794     this.tabs = [];
19795     Roo.bootstrap.TabGroup.register(this);
19796     
19797 };
19798
19799 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
19800     
19801     carousel : false,
19802     transition : false,
19803     bullets : 0,
19804     timer : 0,
19805     autoslide : false,
19806     slideFn : false,
19807     slideOnTouch : false,
19808     showarrow : true,
19809     
19810     getAutoCreate : function()
19811     {
19812         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19813         
19814         cfg.cls += ' tab-content';
19815         
19816         if (this.carousel) {
19817             cfg.cls += ' carousel slide';
19818             
19819             cfg.cn = [{
19820                cls : 'carousel-inner',
19821                cn : []
19822             }];
19823         
19824             if(this.bullets  && !Roo.isTouch){
19825                 
19826                 var bullets = {
19827                     cls : 'carousel-bullets',
19828                     cn : []
19829                 };
19830                
19831                 if(this.bullets_cls){
19832                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19833                 }
19834                 
19835                 bullets.cn.push({
19836                     cls : 'clear'
19837                 });
19838                 
19839                 cfg.cn[0].cn.push(bullets);
19840             }
19841             
19842             if(this.showarrow){
19843                 cfg.cn[0].cn.push({
19844                     tag : 'div',
19845                     class : 'carousel-arrow',
19846                     cn : [
19847                         {
19848                             tag : 'div',
19849                             class : 'carousel-prev',
19850                             cn : [
19851                                 {
19852                                     tag : 'i',
19853                                     class : 'fa fa-chevron-left'
19854                                 }
19855                             ]
19856                         },
19857                         {
19858                             tag : 'div',
19859                             class : 'carousel-next',
19860                             cn : [
19861                                 {
19862                                     tag : 'i',
19863                                     class : 'fa fa-chevron-right'
19864                                 }
19865                             ]
19866                         }
19867                     ]
19868                 });
19869             }
19870             
19871         }
19872         
19873         return cfg;
19874     },
19875     
19876     initEvents:  function()
19877     {
19878 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19879 //            this.el.on("touchstart", this.onTouchStart, this);
19880 //        }
19881         
19882         if(this.autoslide){
19883             var _this = this;
19884             
19885             this.slideFn = window.setInterval(function() {
19886                 _this.showPanelNext();
19887             }, this.timer);
19888         }
19889         
19890         if(this.showarrow){
19891             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
19892             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
19893         }
19894         
19895         
19896     },
19897     
19898 //    onTouchStart : function(e, el, o)
19899 //    {
19900 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
19901 //            return;
19902 //        }
19903 //        
19904 //        this.showPanelNext();
19905 //    },
19906     
19907     
19908     getChildContainer : function()
19909     {
19910         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
19911     },
19912     
19913     /**
19914     * register a Navigation item
19915     * @param {Roo.bootstrap.NavItem} the navitem to add
19916     */
19917     register : function(item)
19918     {
19919         this.tabs.push( item);
19920         item.navId = this.navId; // not really needed..
19921         this.addBullet();
19922     
19923     },
19924     
19925     getActivePanel : function()
19926     {
19927         var r = false;
19928         Roo.each(this.tabs, function(t) {
19929             if (t.active) {
19930                 r = t;
19931                 return false;
19932             }
19933             return null;
19934         });
19935         return r;
19936         
19937     },
19938     getPanelByName : function(n)
19939     {
19940         var r = false;
19941         Roo.each(this.tabs, function(t) {
19942             if (t.tabId == n) {
19943                 r = t;
19944                 return false;
19945             }
19946             return null;
19947         });
19948         return r;
19949     },
19950     indexOfPanel : function(p)
19951     {
19952         var r = false;
19953         Roo.each(this.tabs, function(t,i) {
19954             if (t.tabId == p.tabId) {
19955                 r = i;
19956                 return false;
19957             }
19958             return null;
19959         });
19960         return r;
19961     },
19962     /**
19963      * show a specific panel
19964      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
19965      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
19966      */
19967     showPanel : function (pan)
19968     {
19969         if(this.transition || typeof(pan) == 'undefined'){
19970             Roo.log("waiting for the transitionend");
19971             return false;
19972         }
19973         
19974         if (typeof(pan) == 'number') {
19975             pan = this.tabs[pan];
19976         }
19977         
19978         if (typeof(pan) == 'string') {
19979             pan = this.getPanelByName(pan);
19980         }
19981         
19982         var cur = this.getActivePanel();
19983         
19984         if(!pan || !cur){
19985             Roo.log('pan or acitve pan is undefined');
19986             return false;
19987         }
19988         
19989         if (pan.tabId == this.getActivePanel().tabId) {
19990             return true;
19991         }
19992         
19993         if (false === cur.fireEvent('beforedeactivate')) {
19994             return false;
19995         }
19996         
19997         if(this.bullets > 0 && !Roo.isTouch){
19998             this.setActiveBullet(this.indexOfPanel(pan));
19999         }
20000         
20001         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20002             
20003             //class="carousel-item carousel-item-next carousel-item-left"
20004             
20005             this.transition = true;
20006             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20007             var lr = dir == 'next' ? 'left' : 'right';
20008             pan.el.addClass(dir); // or prev
20009             pan.el.addClass('carousel-item-' + dir); // or prev
20010             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20011             cur.el.addClass(lr); // or right
20012             pan.el.addClass(lr);
20013             cur.el.addClass('carousel-item-' +lr); // or right
20014             pan.el.addClass('carousel-item-' +lr);
20015             
20016             
20017             var _this = this;
20018             cur.el.on('transitionend', function() {
20019                 Roo.log("trans end?");
20020                 
20021                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20022                 pan.setActive(true);
20023                 
20024                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20025                 cur.setActive(false);
20026                 
20027                 _this.transition = false;
20028                 
20029             }, this, { single:  true } );
20030             
20031             return true;
20032         }
20033         
20034         cur.setActive(false);
20035         pan.setActive(true);
20036         
20037         return true;
20038         
20039     },
20040     showPanelNext : function()
20041     {
20042         var i = this.indexOfPanel(this.getActivePanel());
20043         
20044         if (i >= this.tabs.length - 1 && !this.autoslide) {
20045             return;
20046         }
20047         
20048         if (i >= this.tabs.length - 1 && this.autoslide) {
20049             i = -1;
20050         }
20051         
20052         this.showPanel(this.tabs[i+1]);
20053     },
20054     
20055     showPanelPrev : function()
20056     {
20057         var i = this.indexOfPanel(this.getActivePanel());
20058         
20059         if (i  < 1 && !this.autoslide) {
20060             return;
20061         }
20062         
20063         if (i < 1 && this.autoslide) {
20064             i = this.tabs.length;
20065         }
20066         
20067         this.showPanel(this.tabs[i-1]);
20068     },
20069     
20070     
20071     addBullet: function()
20072     {
20073         if(!this.bullets || Roo.isTouch){
20074             return;
20075         }
20076         var ctr = this.el.select('.carousel-bullets',true).first();
20077         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20078         var bullet = ctr.createChild({
20079             cls : 'bullet bullet-' + i
20080         },ctr.dom.lastChild);
20081         
20082         
20083         var _this = this;
20084         
20085         bullet.on('click', (function(e, el, o, ii, t){
20086
20087             e.preventDefault();
20088
20089             this.showPanel(ii);
20090
20091             if(this.autoslide && this.slideFn){
20092                 clearInterval(this.slideFn);
20093                 this.slideFn = window.setInterval(function() {
20094                     _this.showPanelNext();
20095                 }, this.timer);
20096             }
20097
20098         }).createDelegate(this, [i, bullet], true));
20099                 
20100         
20101     },
20102      
20103     setActiveBullet : function(i)
20104     {
20105         if(Roo.isTouch){
20106             return;
20107         }
20108         
20109         Roo.each(this.el.select('.bullet', true).elements, function(el){
20110             el.removeClass('selected');
20111         });
20112
20113         var bullet = this.el.select('.bullet-' + i, true).first();
20114         
20115         if(!bullet){
20116             return;
20117         }
20118         
20119         bullet.addClass('selected');
20120     }
20121     
20122     
20123   
20124 });
20125
20126  
20127
20128  
20129  
20130 Roo.apply(Roo.bootstrap.TabGroup, {
20131     
20132     groups: {},
20133      /**
20134     * register a Navigation Group
20135     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20136     */
20137     register : function(navgrp)
20138     {
20139         this.groups[navgrp.navId] = navgrp;
20140         
20141     },
20142     /**
20143     * fetch a Navigation Group based on the navigation ID
20144     * if one does not exist , it will get created.
20145     * @param {string} the navgroup to add
20146     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20147     */
20148     get: function(navId) {
20149         if (typeof(this.groups[navId]) == 'undefined') {
20150             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20151         }
20152         return this.groups[navId] ;
20153     }
20154     
20155     
20156     
20157 });
20158
20159  /*
20160  * - LGPL
20161  *
20162  * TabPanel
20163  * 
20164  */
20165
20166 /**
20167  * @class Roo.bootstrap.TabPanel
20168  * @extends Roo.bootstrap.Component
20169  * Bootstrap TabPanel class
20170  * @cfg {Boolean} active panel active
20171  * @cfg {String} html panel content
20172  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20173  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20174  * @cfg {String} href click to link..
20175  * 
20176  * 
20177  * @constructor
20178  * Create a new TabPanel
20179  * @param {Object} config The config object
20180  */
20181
20182 Roo.bootstrap.TabPanel = function(config){
20183     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20184     this.addEvents({
20185         /**
20186              * @event changed
20187              * Fires when the active status changes
20188              * @param {Roo.bootstrap.TabPanel} this
20189              * @param {Boolean} state the new state
20190             
20191          */
20192         'changed': true,
20193         /**
20194              * @event beforedeactivate
20195              * Fires before a tab is de-activated - can be used to do validation on a form.
20196              * @param {Roo.bootstrap.TabPanel} this
20197              * @return {Boolean} false if there is an error
20198             
20199          */
20200         'beforedeactivate': true
20201      });
20202     
20203     this.tabId = this.tabId || Roo.id();
20204   
20205 };
20206
20207 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20208     
20209     active: false,
20210     html: false,
20211     tabId: false,
20212     navId : false,
20213     href : '',
20214     
20215     getAutoCreate : function(){
20216         
20217         
20218         var cfg = {
20219             tag: 'div',
20220             // item is needed for carousel - not sure if it has any effect otherwise
20221             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20222             html: this.html || ''
20223         };
20224         
20225         if(this.active){
20226             cfg.cls += ' active';
20227         }
20228         
20229         if(this.tabId){
20230             cfg.tabId = this.tabId;
20231         }
20232         
20233         
20234         
20235         return cfg;
20236     },
20237     
20238     initEvents:  function()
20239     {
20240         var p = this.parent();
20241         
20242         this.navId = this.navId || p.navId;
20243         
20244         if (typeof(this.navId) != 'undefined') {
20245             // not really needed.. but just in case.. parent should be a NavGroup.
20246             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20247             
20248             tg.register(this);
20249             
20250             var i = tg.tabs.length - 1;
20251             
20252             if(this.active && tg.bullets > 0 && i < tg.bullets){
20253                 tg.setActiveBullet(i);
20254             }
20255         }
20256         
20257         this.el.on('click', this.onClick, this);
20258         
20259         if(Roo.isTouch){
20260             this.el.on("touchstart", this.onTouchStart, this);
20261             this.el.on("touchmove", this.onTouchMove, this);
20262             this.el.on("touchend", this.onTouchEnd, this);
20263         }
20264         
20265     },
20266     
20267     onRender : function(ct, position)
20268     {
20269         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20270     },
20271     
20272     setActive : function(state)
20273     {
20274         Roo.log("panel - set active " + this.tabId + "=" + state);
20275         
20276         this.active = state;
20277         if (!state) {
20278             this.el.removeClass('active');
20279             
20280         } else  if (!this.el.hasClass('active')) {
20281             this.el.addClass('active');
20282         }
20283         
20284         this.fireEvent('changed', this, state);
20285     },
20286     
20287     onClick : function(e)
20288     {
20289         e.preventDefault();
20290         
20291         if(!this.href.length){
20292             return;
20293         }
20294         
20295         window.location.href = this.href;
20296     },
20297     
20298     startX : 0,
20299     startY : 0,
20300     endX : 0,
20301     endY : 0,
20302     swiping : false,
20303     
20304     onTouchStart : function(e)
20305     {
20306         this.swiping = false;
20307         
20308         this.startX = e.browserEvent.touches[0].clientX;
20309         this.startY = e.browserEvent.touches[0].clientY;
20310     },
20311     
20312     onTouchMove : function(e)
20313     {
20314         this.swiping = true;
20315         
20316         this.endX = e.browserEvent.touches[0].clientX;
20317         this.endY = e.browserEvent.touches[0].clientY;
20318     },
20319     
20320     onTouchEnd : function(e)
20321     {
20322         if(!this.swiping){
20323             this.onClick(e);
20324             return;
20325         }
20326         
20327         var tabGroup = this.parent();
20328         
20329         if(this.endX > this.startX){ // swiping right
20330             tabGroup.showPanelPrev();
20331             return;
20332         }
20333         
20334         if(this.startX > this.endX){ // swiping left
20335             tabGroup.showPanelNext();
20336             return;
20337         }
20338     }
20339     
20340     
20341 });
20342  
20343
20344  
20345
20346  /*
20347  * - LGPL
20348  *
20349  * DateField
20350  * 
20351  */
20352
20353 /**
20354  * @class Roo.bootstrap.DateField
20355  * @extends Roo.bootstrap.Input
20356  * Bootstrap DateField class
20357  * @cfg {Number} weekStart default 0
20358  * @cfg {String} viewMode default empty, (months|years)
20359  * @cfg {String} minViewMode default empty, (months|years)
20360  * @cfg {Number} startDate default -Infinity
20361  * @cfg {Number} endDate default Infinity
20362  * @cfg {Boolean} todayHighlight default false
20363  * @cfg {Boolean} todayBtn default false
20364  * @cfg {Boolean} calendarWeeks default false
20365  * @cfg {Object} daysOfWeekDisabled default empty
20366  * @cfg {Boolean} singleMode default false (true | false)
20367  * 
20368  * @cfg {Boolean} keyboardNavigation default true
20369  * @cfg {String} language default en
20370  * 
20371  * @constructor
20372  * Create a new DateField
20373  * @param {Object} config The config object
20374  */
20375
20376 Roo.bootstrap.DateField = function(config){
20377     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20378      this.addEvents({
20379             /**
20380              * @event show
20381              * Fires when this field show.
20382              * @param {Roo.bootstrap.DateField} this
20383              * @param {Mixed} date The date value
20384              */
20385             show : true,
20386             /**
20387              * @event show
20388              * Fires when this field hide.
20389              * @param {Roo.bootstrap.DateField} this
20390              * @param {Mixed} date The date value
20391              */
20392             hide : true,
20393             /**
20394              * @event select
20395              * Fires when select a date.
20396              * @param {Roo.bootstrap.DateField} this
20397              * @param {Mixed} date The date value
20398              */
20399             select : true,
20400             /**
20401              * @event beforeselect
20402              * Fires when before select a date.
20403              * @param {Roo.bootstrap.DateField} this
20404              * @param {Mixed} date The date value
20405              */
20406             beforeselect : true
20407         });
20408 };
20409
20410 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20411     
20412     /**
20413      * @cfg {String} format
20414      * The default date format string which can be overriden for localization support.  The format must be
20415      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20416      */
20417     format : "m/d/y",
20418     /**
20419      * @cfg {String} altFormats
20420      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20421      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20422      */
20423     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20424     
20425     weekStart : 0,
20426     
20427     viewMode : '',
20428     
20429     minViewMode : '',
20430     
20431     todayHighlight : false,
20432     
20433     todayBtn: false,
20434     
20435     language: 'en',
20436     
20437     keyboardNavigation: true,
20438     
20439     calendarWeeks: false,
20440     
20441     startDate: -Infinity,
20442     
20443     endDate: Infinity,
20444     
20445     daysOfWeekDisabled: [],
20446     
20447     _events: [],
20448     
20449     singleMode : false,
20450     
20451     UTCDate: function()
20452     {
20453         return new Date(Date.UTC.apply(Date, arguments));
20454     },
20455     
20456     UTCToday: function()
20457     {
20458         var today = new Date();
20459         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20460     },
20461     
20462     getDate: function() {
20463             var d = this.getUTCDate();
20464             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20465     },
20466     
20467     getUTCDate: function() {
20468             return this.date;
20469     },
20470     
20471     setDate: function(d) {
20472             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20473     },
20474     
20475     setUTCDate: function(d) {
20476             this.date = d;
20477             this.setValue(this.formatDate(this.date));
20478     },
20479         
20480     onRender: function(ct, position)
20481     {
20482         
20483         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20484         
20485         this.language = this.language || 'en';
20486         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20487         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20488         
20489         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20490         this.format = this.format || 'm/d/y';
20491         this.isInline = false;
20492         this.isInput = true;
20493         this.component = this.el.select('.add-on', true).first() || false;
20494         this.component = (this.component && this.component.length === 0) ? false : this.component;
20495         this.hasInput = this.component && this.inputEl().length;
20496         
20497         if (typeof(this.minViewMode === 'string')) {
20498             switch (this.minViewMode) {
20499                 case 'months':
20500                     this.minViewMode = 1;
20501                     break;
20502                 case 'years':
20503                     this.minViewMode = 2;
20504                     break;
20505                 default:
20506                     this.minViewMode = 0;
20507                     break;
20508             }
20509         }
20510         
20511         if (typeof(this.viewMode === 'string')) {
20512             switch (this.viewMode) {
20513                 case 'months':
20514                     this.viewMode = 1;
20515                     break;
20516                 case 'years':
20517                     this.viewMode = 2;
20518                     break;
20519                 default:
20520                     this.viewMode = 0;
20521                     break;
20522             }
20523         }
20524                 
20525         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20526         
20527 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20528         
20529         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20530         
20531         this.picker().on('mousedown', this.onMousedown, this);
20532         this.picker().on('click', this.onClick, this);
20533         
20534         this.picker().addClass('datepicker-dropdown');
20535         
20536         this.startViewMode = this.viewMode;
20537         
20538         if(this.singleMode){
20539             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20540                 v.setVisibilityMode(Roo.Element.DISPLAY);
20541                 v.hide();
20542             });
20543             
20544             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20545                 v.setStyle('width', '189px');
20546             });
20547         }
20548         
20549         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20550             if(!this.calendarWeeks){
20551                 v.remove();
20552                 return;
20553             }
20554             
20555             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20556             v.attr('colspan', function(i, val){
20557                 return parseInt(val) + 1;
20558             });
20559         });
20560                         
20561         
20562         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20563         
20564         this.setStartDate(this.startDate);
20565         this.setEndDate(this.endDate);
20566         
20567         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20568         
20569         this.fillDow();
20570         this.fillMonths();
20571         this.update();
20572         this.showMode();
20573         
20574         if(this.isInline) {
20575             this.showPopup();
20576         }
20577     },
20578     
20579     picker : function()
20580     {
20581         return this.pickerEl;
20582 //        return this.el.select('.datepicker', true).first();
20583     },
20584     
20585     fillDow: function()
20586     {
20587         var dowCnt = this.weekStart;
20588         
20589         var dow = {
20590             tag: 'tr',
20591             cn: [
20592                 
20593             ]
20594         };
20595         
20596         if(this.calendarWeeks){
20597             dow.cn.push({
20598                 tag: 'th',
20599                 cls: 'cw',
20600                 html: '&nbsp;'
20601             })
20602         }
20603         
20604         while (dowCnt < this.weekStart + 7) {
20605             dow.cn.push({
20606                 tag: 'th',
20607                 cls: 'dow',
20608                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20609             });
20610         }
20611         
20612         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20613     },
20614     
20615     fillMonths: function()
20616     {    
20617         var i = 0;
20618         var months = this.picker().select('>.datepicker-months td', true).first();
20619         
20620         months.dom.innerHTML = '';
20621         
20622         while (i < 12) {
20623             var month = {
20624                 tag: 'span',
20625                 cls: 'month',
20626                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20627             };
20628             
20629             months.createChild(month);
20630         }
20631         
20632     },
20633     
20634     update: function()
20635     {
20636         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;
20637         
20638         if (this.date < this.startDate) {
20639             this.viewDate = new Date(this.startDate);
20640         } else if (this.date > this.endDate) {
20641             this.viewDate = new Date(this.endDate);
20642         } else {
20643             this.viewDate = new Date(this.date);
20644         }
20645         
20646         this.fill();
20647     },
20648     
20649     fill: function() 
20650     {
20651         var d = new Date(this.viewDate),
20652                 year = d.getUTCFullYear(),
20653                 month = d.getUTCMonth(),
20654                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20655                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20656                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20657                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20658                 currentDate = this.date && this.date.valueOf(),
20659                 today = this.UTCToday();
20660         
20661         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20662         
20663 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20664         
20665 //        this.picker.select('>tfoot th.today').
20666 //                                              .text(dates[this.language].today)
20667 //                                              .toggle(this.todayBtn !== false);
20668     
20669         this.updateNavArrows();
20670         this.fillMonths();
20671                                                 
20672         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20673         
20674         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20675          
20676         prevMonth.setUTCDate(day);
20677         
20678         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20679         
20680         var nextMonth = new Date(prevMonth);
20681         
20682         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20683         
20684         nextMonth = nextMonth.valueOf();
20685         
20686         var fillMonths = false;
20687         
20688         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20689         
20690         while(prevMonth.valueOf() <= nextMonth) {
20691             var clsName = '';
20692             
20693             if (prevMonth.getUTCDay() === this.weekStart) {
20694                 if(fillMonths){
20695                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20696                 }
20697                     
20698                 fillMonths = {
20699                     tag: 'tr',
20700                     cn: []
20701                 };
20702                 
20703                 if(this.calendarWeeks){
20704                     // ISO 8601: First week contains first thursday.
20705                     // ISO also states week starts on Monday, but we can be more abstract here.
20706                     var
20707                     // Start of current week: based on weekstart/current date
20708                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20709                     // Thursday of this week
20710                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20711                     // First Thursday of year, year from thursday
20712                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20713                     // Calendar week: ms between thursdays, div ms per day, div 7 days
20714                     calWeek =  (th - yth) / 864e5 / 7 + 1;
20715                     
20716                     fillMonths.cn.push({
20717                         tag: 'td',
20718                         cls: 'cw',
20719                         html: calWeek
20720                     });
20721                 }
20722             }
20723             
20724             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20725                 clsName += ' old';
20726             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20727                 clsName += ' new';
20728             }
20729             if (this.todayHighlight &&
20730                 prevMonth.getUTCFullYear() == today.getFullYear() &&
20731                 prevMonth.getUTCMonth() == today.getMonth() &&
20732                 prevMonth.getUTCDate() == today.getDate()) {
20733                 clsName += ' today';
20734             }
20735             
20736             if (currentDate && prevMonth.valueOf() === currentDate) {
20737                 clsName += ' active';
20738             }
20739             
20740             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20741                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20742                     clsName += ' disabled';
20743             }
20744             
20745             fillMonths.cn.push({
20746                 tag: 'td',
20747                 cls: 'day ' + clsName,
20748                 html: prevMonth.getDate()
20749             });
20750             
20751             prevMonth.setDate(prevMonth.getDate()+1);
20752         }
20753           
20754         var currentYear = this.date && this.date.getUTCFullYear();
20755         var currentMonth = this.date && this.date.getUTCMonth();
20756         
20757         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20758         
20759         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20760             v.removeClass('active');
20761             
20762             if(currentYear === year && k === currentMonth){
20763                 v.addClass('active');
20764             }
20765             
20766             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20767                 v.addClass('disabled');
20768             }
20769             
20770         });
20771         
20772         
20773         year = parseInt(year/10, 10) * 10;
20774         
20775         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20776         
20777         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20778         
20779         year -= 1;
20780         for (var i = -1; i < 11; i++) {
20781             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20782                 tag: 'span',
20783                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20784                 html: year
20785             });
20786             
20787             year += 1;
20788         }
20789     },
20790     
20791     showMode: function(dir) 
20792     {
20793         if (dir) {
20794             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20795         }
20796         
20797         Roo.each(this.picker().select('>div',true).elements, function(v){
20798             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20799             v.hide();
20800         });
20801         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20802     },
20803     
20804     place: function()
20805     {
20806         if(this.isInline) {
20807             return;
20808         }
20809         
20810         this.picker().removeClass(['bottom', 'top']);
20811         
20812         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20813             /*
20814              * place to the top of element!
20815              *
20816              */
20817             
20818             this.picker().addClass('top');
20819             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20820             
20821             return;
20822         }
20823         
20824         this.picker().addClass('bottom');
20825         
20826         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20827     },
20828     
20829     parseDate : function(value)
20830     {
20831         if(!value || value instanceof Date){
20832             return value;
20833         }
20834         var v = Date.parseDate(value, this.format);
20835         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20836             v = Date.parseDate(value, 'Y-m-d');
20837         }
20838         if(!v && this.altFormats){
20839             if(!this.altFormatsArray){
20840                 this.altFormatsArray = this.altFormats.split("|");
20841             }
20842             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20843                 v = Date.parseDate(value, this.altFormatsArray[i]);
20844             }
20845         }
20846         return v;
20847     },
20848     
20849     formatDate : function(date, fmt)
20850     {   
20851         return (!date || !(date instanceof Date)) ?
20852         date : date.dateFormat(fmt || this.format);
20853     },
20854     
20855     onFocus : function()
20856     {
20857         Roo.bootstrap.DateField.superclass.onFocus.call(this);
20858         this.showPopup();
20859     },
20860     
20861     onBlur : function()
20862     {
20863         Roo.bootstrap.DateField.superclass.onBlur.call(this);
20864         
20865         var d = this.inputEl().getValue();
20866         
20867         this.setValue(d);
20868                 
20869         this.hidePopup();
20870     },
20871     
20872     showPopup : function()
20873     {
20874         this.picker().show();
20875         this.update();
20876         this.place();
20877         
20878         this.fireEvent('showpopup', this, this.date);
20879     },
20880     
20881     hidePopup : function()
20882     {
20883         if(this.isInline) {
20884             return;
20885         }
20886         this.picker().hide();
20887         this.viewMode = this.startViewMode;
20888         this.showMode();
20889         
20890         this.fireEvent('hidepopup', this, this.date);
20891         
20892     },
20893     
20894     onMousedown: function(e)
20895     {
20896         e.stopPropagation();
20897         e.preventDefault();
20898     },
20899     
20900     keyup: function(e)
20901     {
20902         Roo.bootstrap.DateField.superclass.keyup.call(this);
20903         this.update();
20904     },
20905
20906     setValue: function(v)
20907     {
20908         if(this.fireEvent('beforeselect', this, v) !== false){
20909             var d = new Date(this.parseDate(v) ).clearTime();
20910         
20911             if(isNaN(d.getTime())){
20912                 this.date = this.viewDate = '';
20913                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20914                 return;
20915             }
20916
20917             v = this.formatDate(d);
20918
20919             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
20920
20921             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
20922
20923             this.update();
20924
20925             this.fireEvent('select', this, this.date);
20926         }
20927     },
20928     
20929     getValue: function()
20930     {
20931         return this.formatDate(this.date);
20932     },
20933     
20934     fireKey: function(e)
20935     {
20936         if (!this.picker().isVisible()){
20937             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20938                 this.showPopup();
20939             }
20940             return;
20941         }
20942         
20943         var dateChanged = false,
20944         dir, day, month,
20945         newDate, newViewDate;
20946         
20947         switch(e.keyCode){
20948             case 27: // escape
20949                 this.hidePopup();
20950                 e.preventDefault();
20951                 break;
20952             case 37: // left
20953             case 39: // right
20954                 if (!this.keyboardNavigation) {
20955                     break;
20956                 }
20957                 dir = e.keyCode == 37 ? -1 : 1;
20958                 
20959                 if (e.ctrlKey){
20960                     newDate = this.moveYear(this.date, dir);
20961                     newViewDate = this.moveYear(this.viewDate, dir);
20962                 } else if (e.shiftKey){
20963                     newDate = this.moveMonth(this.date, dir);
20964                     newViewDate = this.moveMonth(this.viewDate, dir);
20965                 } else {
20966                     newDate = new Date(this.date);
20967                     newDate.setUTCDate(this.date.getUTCDate() + dir);
20968                     newViewDate = new Date(this.viewDate);
20969                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
20970                 }
20971                 if (this.dateWithinRange(newDate)){
20972                     this.date = newDate;
20973                     this.viewDate = newViewDate;
20974                     this.setValue(this.formatDate(this.date));
20975 //                    this.update();
20976                     e.preventDefault();
20977                     dateChanged = true;
20978                 }
20979                 break;
20980             case 38: // up
20981             case 40: // down
20982                 if (!this.keyboardNavigation) {
20983                     break;
20984                 }
20985                 dir = e.keyCode == 38 ? -1 : 1;
20986                 if (e.ctrlKey){
20987                     newDate = this.moveYear(this.date, dir);
20988                     newViewDate = this.moveYear(this.viewDate, dir);
20989                 } else if (e.shiftKey){
20990                     newDate = this.moveMonth(this.date, dir);
20991                     newViewDate = this.moveMonth(this.viewDate, dir);
20992                 } else {
20993                     newDate = new Date(this.date);
20994                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
20995                     newViewDate = new Date(this.viewDate);
20996                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
20997                 }
20998                 if (this.dateWithinRange(newDate)){
20999                     this.date = newDate;
21000                     this.viewDate = newViewDate;
21001                     this.setValue(this.formatDate(this.date));
21002 //                    this.update();
21003                     e.preventDefault();
21004                     dateChanged = true;
21005                 }
21006                 break;
21007             case 13: // enter
21008                 this.setValue(this.formatDate(this.date));
21009                 this.hidePopup();
21010                 e.preventDefault();
21011                 break;
21012             case 9: // tab
21013                 this.setValue(this.formatDate(this.date));
21014                 this.hidePopup();
21015                 break;
21016             case 16: // shift
21017             case 17: // ctrl
21018             case 18: // alt
21019                 break;
21020             default :
21021                 this.hidePopup();
21022                 
21023         }
21024     },
21025     
21026     
21027     onClick: function(e) 
21028     {
21029         e.stopPropagation();
21030         e.preventDefault();
21031         
21032         var target = e.getTarget();
21033         
21034         if(target.nodeName.toLowerCase() === 'i'){
21035             target = Roo.get(target).dom.parentNode;
21036         }
21037         
21038         var nodeName = target.nodeName;
21039         var className = target.className;
21040         var html = target.innerHTML;
21041         //Roo.log(nodeName);
21042         
21043         switch(nodeName.toLowerCase()) {
21044             case 'th':
21045                 switch(className) {
21046                     case 'switch':
21047                         this.showMode(1);
21048                         break;
21049                     case 'prev':
21050                     case 'next':
21051                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21052                         switch(this.viewMode){
21053                                 case 0:
21054                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21055                                         break;
21056                                 case 1:
21057                                 case 2:
21058                                         this.viewDate = this.moveYear(this.viewDate, dir);
21059                                         break;
21060                         }
21061                         this.fill();
21062                         break;
21063                     case 'today':
21064                         var date = new Date();
21065                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21066 //                        this.fill()
21067                         this.setValue(this.formatDate(this.date));
21068                         
21069                         this.hidePopup();
21070                         break;
21071                 }
21072                 break;
21073             case 'span':
21074                 if (className.indexOf('disabled') < 0) {
21075                     this.viewDate.setUTCDate(1);
21076                     if (className.indexOf('month') > -1) {
21077                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21078                     } else {
21079                         var year = parseInt(html, 10) || 0;
21080                         this.viewDate.setUTCFullYear(year);
21081                         
21082                     }
21083                     
21084                     if(this.singleMode){
21085                         this.setValue(this.formatDate(this.viewDate));
21086                         this.hidePopup();
21087                         return;
21088                     }
21089                     
21090                     this.showMode(-1);
21091                     this.fill();
21092                 }
21093                 break;
21094                 
21095             case 'td':
21096                 //Roo.log(className);
21097                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21098                     var day = parseInt(html, 10) || 1;
21099                     var year = this.viewDate.getUTCFullYear(),
21100                         month = this.viewDate.getUTCMonth();
21101
21102                     if (className.indexOf('old') > -1) {
21103                         if(month === 0 ){
21104                             month = 11;
21105                             year -= 1;
21106                         }else{
21107                             month -= 1;
21108                         }
21109                     } else if (className.indexOf('new') > -1) {
21110                         if (month == 11) {
21111                             month = 0;
21112                             year += 1;
21113                         } else {
21114                             month += 1;
21115                         }
21116                     }
21117                     //Roo.log([year,month,day]);
21118                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21119                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21120 //                    this.fill();
21121                     //Roo.log(this.formatDate(this.date));
21122                     this.setValue(this.formatDate(this.date));
21123                     this.hidePopup();
21124                 }
21125                 break;
21126         }
21127     },
21128     
21129     setStartDate: function(startDate)
21130     {
21131         this.startDate = startDate || -Infinity;
21132         if (this.startDate !== -Infinity) {
21133             this.startDate = this.parseDate(this.startDate);
21134         }
21135         this.update();
21136         this.updateNavArrows();
21137     },
21138
21139     setEndDate: function(endDate)
21140     {
21141         this.endDate = endDate || Infinity;
21142         if (this.endDate !== Infinity) {
21143             this.endDate = this.parseDate(this.endDate);
21144         }
21145         this.update();
21146         this.updateNavArrows();
21147     },
21148     
21149     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21150     {
21151         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21152         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21153             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21154         }
21155         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21156             return parseInt(d, 10);
21157         });
21158         this.update();
21159         this.updateNavArrows();
21160     },
21161     
21162     updateNavArrows: function() 
21163     {
21164         if(this.singleMode){
21165             return;
21166         }
21167         
21168         var d = new Date(this.viewDate),
21169         year = d.getUTCFullYear(),
21170         month = d.getUTCMonth();
21171         
21172         Roo.each(this.picker().select('.prev', true).elements, function(v){
21173             v.show();
21174             switch (this.viewMode) {
21175                 case 0:
21176
21177                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21178                         v.hide();
21179                     }
21180                     break;
21181                 case 1:
21182                 case 2:
21183                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21184                         v.hide();
21185                     }
21186                     break;
21187             }
21188         });
21189         
21190         Roo.each(this.picker().select('.next', true).elements, function(v){
21191             v.show();
21192             switch (this.viewMode) {
21193                 case 0:
21194
21195                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21196                         v.hide();
21197                     }
21198                     break;
21199                 case 1:
21200                 case 2:
21201                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21202                         v.hide();
21203                     }
21204                     break;
21205             }
21206         })
21207     },
21208     
21209     moveMonth: function(date, dir)
21210     {
21211         if (!dir) {
21212             return date;
21213         }
21214         var new_date = new Date(date.valueOf()),
21215         day = new_date.getUTCDate(),
21216         month = new_date.getUTCMonth(),
21217         mag = Math.abs(dir),
21218         new_month, test;
21219         dir = dir > 0 ? 1 : -1;
21220         if (mag == 1){
21221             test = dir == -1
21222             // If going back one month, make sure month is not current month
21223             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21224             ? function(){
21225                 return new_date.getUTCMonth() == month;
21226             }
21227             // If going forward one month, make sure month is as expected
21228             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21229             : function(){
21230                 return new_date.getUTCMonth() != new_month;
21231             };
21232             new_month = month + dir;
21233             new_date.setUTCMonth(new_month);
21234             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21235             if (new_month < 0 || new_month > 11) {
21236                 new_month = (new_month + 12) % 12;
21237             }
21238         } else {
21239             // For magnitudes >1, move one month at a time...
21240             for (var i=0; i<mag; i++) {
21241                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21242                 new_date = this.moveMonth(new_date, dir);
21243             }
21244             // ...then reset the day, keeping it in the new month
21245             new_month = new_date.getUTCMonth();
21246             new_date.setUTCDate(day);
21247             test = function(){
21248                 return new_month != new_date.getUTCMonth();
21249             };
21250         }
21251         // Common date-resetting loop -- if date is beyond end of month, make it
21252         // end of month
21253         while (test()){
21254             new_date.setUTCDate(--day);
21255             new_date.setUTCMonth(new_month);
21256         }
21257         return new_date;
21258     },
21259
21260     moveYear: function(date, dir)
21261     {
21262         return this.moveMonth(date, dir*12);
21263     },
21264
21265     dateWithinRange: function(date)
21266     {
21267         return date >= this.startDate && date <= this.endDate;
21268     },
21269
21270     
21271     remove: function() 
21272     {
21273         this.picker().remove();
21274     },
21275     
21276     validateValue : function(value)
21277     {
21278         if(this.getVisibilityEl().hasClass('hidden')){
21279             return true;
21280         }
21281         
21282         if(value.length < 1)  {
21283             if(this.allowBlank){
21284                 return true;
21285             }
21286             return false;
21287         }
21288         
21289         if(value.length < this.minLength){
21290             return false;
21291         }
21292         if(value.length > this.maxLength){
21293             return false;
21294         }
21295         if(this.vtype){
21296             var vt = Roo.form.VTypes;
21297             if(!vt[this.vtype](value, this)){
21298                 return false;
21299             }
21300         }
21301         if(typeof this.validator == "function"){
21302             var msg = this.validator(value);
21303             if(msg !== true){
21304                 return false;
21305             }
21306         }
21307         
21308         if(this.regex && !this.regex.test(value)){
21309             return false;
21310         }
21311         
21312         if(typeof(this.parseDate(value)) == 'undefined'){
21313             return false;
21314         }
21315         
21316         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21317             return false;
21318         }      
21319         
21320         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21321             return false;
21322         } 
21323         
21324         
21325         return true;
21326     },
21327     
21328     reset : function()
21329     {
21330         this.date = this.viewDate = '';
21331         
21332         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21333     }
21334    
21335 });
21336
21337 Roo.apply(Roo.bootstrap.DateField,  {
21338     
21339     head : {
21340         tag: 'thead',
21341         cn: [
21342         {
21343             tag: 'tr',
21344             cn: [
21345             {
21346                 tag: 'th',
21347                 cls: 'prev',
21348                 html: '<i class="fa fa-arrow-left"/>'
21349             },
21350             {
21351                 tag: 'th',
21352                 cls: 'switch',
21353                 colspan: '5'
21354             },
21355             {
21356                 tag: 'th',
21357                 cls: 'next',
21358                 html: '<i class="fa fa-arrow-right"/>'
21359             }
21360
21361             ]
21362         }
21363         ]
21364     },
21365     
21366     content : {
21367         tag: 'tbody',
21368         cn: [
21369         {
21370             tag: 'tr',
21371             cn: [
21372             {
21373                 tag: 'td',
21374                 colspan: '7'
21375             }
21376             ]
21377         }
21378         ]
21379     },
21380     
21381     footer : {
21382         tag: 'tfoot',
21383         cn: [
21384         {
21385             tag: 'tr',
21386             cn: [
21387             {
21388                 tag: 'th',
21389                 colspan: '7',
21390                 cls: 'today'
21391             }
21392                     
21393             ]
21394         }
21395         ]
21396     },
21397     
21398     dates:{
21399         en: {
21400             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21401             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21402             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21403             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21404             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21405             today: "Today"
21406         }
21407     },
21408     
21409     modes: [
21410     {
21411         clsName: 'days',
21412         navFnc: 'Month',
21413         navStep: 1
21414     },
21415     {
21416         clsName: 'months',
21417         navFnc: 'FullYear',
21418         navStep: 1
21419     },
21420     {
21421         clsName: 'years',
21422         navFnc: 'FullYear',
21423         navStep: 10
21424     }]
21425 });
21426
21427 Roo.apply(Roo.bootstrap.DateField,  {
21428   
21429     template : {
21430         tag: 'div',
21431         cls: 'datepicker dropdown-menu roo-dynamic',
21432         cn: [
21433         {
21434             tag: 'div',
21435             cls: 'datepicker-days',
21436             cn: [
21437             {
21438                 tag: 'table',
21439                 cls: 'table-condensed',
21440                 cn:[
21441                 Roo.bootstrap.DateField.head,
21442                 {
21443                     tag: 'tbody'
21444                 },
21445                 Roo.bootstrap.DateField.footer
21446                 ]
21447             }
21448             ]
21449         },
21450         {
21451             tag: 'div',
21452             cls: 'datepicker-months',
21453             cn: [
21454             {
21455                 tag: 'table',
21456                 cls: 'table-condensed',
21457                 cn:[
21458                 Roo.bootstrap.DateField.head,
21459                 Roo.bootstrap.DateField.content,
21460                 Roo.bootstrap.DateField.footer
21461                 ]
21462             }
21463             ]
21464         },
21465         {
21466             tag: 'div',
21467             cls: 'datepicker-years',
21468             cn: [
21469             {
21470                 tag: 'table',
21471                 cls: 'table-condensed',
21472                 cn:[
21473                 Roo.bootstrap.DateField.head,
21474                 Roo.bootstrap.DateField.content,
21475                 Roo.bootstrap.DateField.footer
21476                 ]
21477             }
21478             ]
21479         }
21480         ]
21481     }
21482 });
21483
21484  
21485
21486  /*
21487  * - LGPL
21488  *
21489  * TimeField
21490  * 
21491  */
21492
21493 /**
21494  * @class Roo.bootstrap.TimeField
21495  * @extends Roo.bootstrap.Input
21496  * Bootstrap DateField class
21497  * 
21498  * 
21499  * @constructor
21500  * Create a new TimeField
21501  * @param {Object} config The config object
21502  */
21503
21504 Roo.bootstrap.TimeField = function(config){
21505     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21506     this.addEvents({
21507             /**
21508              * @event show
21509              * Fires when this field show.
21510              * @param {Roo.bootstrap.DateField} thisthis
21511              * @param {Mixed} date The date value
21512              */
21513             show : true,
21514             /**
21515              * @event show
21516              * Fires when this field hide.
21517              * @param {Roo.bootstrap.DateField} this
21518              * @param {Mixed} date The date value
21519              */
21520             hide : true,
21521             /**
21522              * @event select
21523              * Fires when select a date.
21524              * @param {Roo.bootstrap.DateField} this
21525              * @param {Mixed} date The date value
21526              */
21527             select : true
21528         });
21529 };
21530
21531 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21532     
21533     /**
21534      * @cfg {String} format
21535      * The default time format string which can be overriden for localization support.  The format must be
21536      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21537      */
21538     format : "H:i",
21539        
21540     onRender: function(ct, position)
21541     {
21542         
21543         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21544                 
21545         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21546         
21547         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21548         
21549         this.pop = this.picker().select('>.datepicker-time',true).first();
21550         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21551         
21552         this.picker().on('mousedown', this.onMousedown, this);
21553         this.picker().on('click', this.onClick, this);
21554         
21555         this.picker().addClass('datepicker-dropdown');
21556     
21557         this.fillTime();
21558         this.update();
21559             
21560         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21561         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21562         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21563         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21564         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21565         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21566
21567     },
21568     
21569     fireKey: function(e){
21570         if (!this.picker().isVisible()){
21571             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21572                 this.show();
21573             }
21574             return;
21575         }
21576
21577         e.preventDefault();
21578         
21579         switch(e.keyCode){
21580             case 27: // escape
21581                 this.hide();
21582                 break;
21583             case 37: // left
21584             case 39: // right
21585                 this.onTogglePeriod();
21586                 break;
21587             case 38: // up
21588                 this.onIncrementMinutes();
21589                 break;
21590             case 40: // down
21591                 this.onDecrementMinutes();
21592                 break;
21593             case 13: // enter
21594             case 9: // tab
21595                 this.setTime();
21596                 break;
21597         }
21598     },
21599     
21600     onClick: function(e) {
21601         e.stopPropagation();
21602         e.preventDefault();
21603     },
21604     
21605     picker : function()
21606     {
21607         return this.el.select('.datepicker', true).first();
21608     },
21609     
21610     fillTime: function()
21611     {    
21612         var time = this.pop.select('tbody', true).first();
21613         
21614         time.dom.innerHTML = '';
21615         
21616         time.createChild({
21617             tag: 'tr',
21618             cn: [
21619                 {
21620                     tag: 'td',
21621                     cn: [
21622                         {
21623                             tag: 'a',
21624                             href: '#',
21625                             cls: 'btn',
21626                             cn: [
21627                                 {
21628                                     tag: 'span',
21629                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
21630                                 }
21631                             ]
21632                         } 
21633                     ]
21634                 },
21635                 {
21636                     tag: 'td',
21637                     cls: 'separator'
21638                 },
21639                 {
21640                     tag: 'td',
21641                     cn: [
21642                         {
21643                             tag: 'a',
21644                             href: '#',
21645                             cls: 'btn',
21646                             cn: [
21647                                 {
21648                                     tag: 'span',
21649                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
21650                                 }
21651                             ]
21652                         }
21653                     ]
21654                 },
21655                 {
21656                     tag: 'td',
21657                     cls: 'separator'
21658                 }
21659             ]
21660         });
21661         
21662         time.createChild({
21663             tag: 'tr',
21664             cn: [
21665                 {
21666                     tag: 'td',
21667                     cn: [
21668                         {
21669                             tag: 'span',
21670                             cls: 'timepicker-hour',
21671                             html: '00'
21672                         }  
21673                     ]
21674                 },
21675                 {
21676                     tag: 'td',
21677                     cls: 'separator',
21678                     html: ':'
21679                 },
21680                 {
21681                     tag: 'td',
21682                     cn: [
21683                         {
21684                             tag: 'span',
21685                             cls: 'timepicker-minute',
21686                             html: '00'
21687                         }  
21688                     ]
21689                 },
21690                 {
21691                     tag: 'td',
21692                     cls: 'separator'
21693                 },
21694                 {
21695                     tag: 'td',
21696                     cn: [
21697                         {
21698                             tag: 'button',
21699                             type: 'button',
21700                             cls: 'btn btn-primary period',
21701                             html: 'AM'
21702                             
21703                         }
21704                     ]
21705                 }
21706             ]
21707         });
21708         
21709         time.createChild({
21710             tag: 'tr',
21711             cn: [
21712                 {
21713                     tag: 'td',
21714                     cn: [
21715                         {
21716                             tag: 'a',
21717                             href: '#',
21718                             cls: 'btn',
21719                             cn: [
21720                                 {
21721                                     tag: 'span',
21722                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
21723                                 }
21724                             ]
21725                         }
21726                     ]
21727                 },
21728                 {
21729                     tag: 'td',
21730                     cls: 'separator'
21731                 },
21732                 {
21733                     tag: 'td',
21734                     cn: [
21735                         {
21736                             tag: 'a',
21737                             href: '#',
21738                             cls: 'btn',
21739                             cn: [
21740                                 {
21741                                     tag: 'span',
21742                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
21743                                 }
21744                             ]
21745                         }
21746                     ]
21747                 },
21748                 {
21749                     tag: 'td',
21750                     cls: 'separator'
21751                 }
21752             ]
21753         });
21754         
21755     },
21756     
21757     update: function()
21758     {
21759         
21760         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21761         
21762         this.fill();
21763     },
21764     
21765     fill: function() 
21766     {
21767         var hours = this.time.getHours();
21768         var minutes = this.time.getMinutes();
21769         var period = 'AM';
21770         
21771         if(hours > 11){
21772             period = 'PM';
21773         }
21774         
21775         if(hours == 0){
21776             hours = 12;
21777         }
21778         
21779         
21780         if(hours > 12){
21781             hours = hours - 12;
21782         }
21783         
21784         if(hours < 10){
21785             hours = '0' + hours;
21786         }
21787         
21788         if(minutes < 10){
21789             minutes = '0' + minutes;
21790         }
21791         
21792         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21793         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21794         this.pop.select('button', true).first().dom.innerHTML = period;
21795         
21796     },
21797     
21798     place: function()
21799     {   
21800         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21801         
21802         var cls = ['bottom'];
21803         
21804         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21805             cls.pop();
21806             cls.push('top');
21807         }
21808         
21809         cls.push('right');
21810         
21811         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21812             cls.pop();
21813             cls.push('left');
21814         }
21815         
21816         this.picker().addClass(cls.join('-'));
21817         
21818         var _this = this;
21819         
21820         Roo.each(cls, function(c){
21821             if(c == 'bottom'){
21822                 _this.picker().setTop(_this.inputEl().getHeight());
21823                 return;
21824             }
21825             if(c == 'top'){
21826                 _this.picker().setTop(0 - _this.picker().getHeight());
21827                 return;
21828             }
21829             
21830             if(c == 'left'){
21831                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21832                 return;
21833             }
21834             if(c == 'right'){
21835                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21836                 return;
21837             }
21838         });
21839         
21840     },
21841   
21842     onFocus : function()
21843     {
21844         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21845         this.show();
21846     },
21847     
21848     onBlur : function()
21849     {
21850         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21851         this.hide();
21852     },
21853     
21854     show : function()
21855     {
21856         this.picker().show();
21857         this.pop.show();
21858         this.update();
21859         this.place();
21860         
21861         this.fireEvent('show', this, this.date);
21862     },
21863     
21864     hide : function()
21865     {
21866         this.picker().hide();
21867         this.pop.hide();
21868         
21869         this.fireEvent('hide', this, this.date);
21870     },
21871     
21872     setTime : function()
21873     {
21874         this.hide();
21875         this.setValue(this.time.format(this.format));
21876         
21877         this.fireEvent('select', this, this.date);
21878         
21879         
21880     },
21881     
21882     onMousedown: function(e){
21883         e.stopPropagation();
21884         e.preventDefault();
21885     },
21886     
21887     onIncrementHours: function()
21888     {
21889         Roo.log('onIncrementHours');
21890         this.time = this.time.add(Date.HOUR, 1);
21891         this.update();
21892         
21893     },
21894     
21895     onDecrementHours: function()
21896     {
21897         Roo.log('onDecrementHours');
21898         this.time = this.time.add(Date.HOUR, -1);
21899         this.update();
21900     },
21901     
21902     onIncrementMinutes: function()
21903     {
21904         Roo.log('onIncrementMinutes');
21905         this.time = this.time.add(Date.MINUTE, 1);
21906         this.update();
21907     },
21908     
21909     onDecrementMinutes: function()
21910     {
21911         Roo.log('onDecrementMinutes');
21912         this.time = this.time.add(Date.MINUTE, -1);
21913         this.update();
21914     },
21915     
21916     onTogglePeriod: function()
21917     {
21918         Roo.log('onTogglePeriod');
21919         this.time = this.time.add(Date.HOUR, 12);
21920         this.update();
21921     }
21922     
21923    
21924 });
21925
21926 Roo.apply(Roo.bootstrap.TimeField,  {
21927     
21928     content : {
21929         tag: 'tbody',
21930         cn: [
21931             {
21932                 tag: 'tr',
21933                 cn: [
21934                 {
21935                     tag: 'td',
21936                     colspan: '7'
21937                 }
21938                 ]
21939             }
21940         ]
21941     },
21942     
21943     footer : {
21944         tag: 'tfoot',
21945         cn: [
21946             {
21947                 tag: 'tr',
21948                 cn: [
21949                 {
21950                     tag: 'th',
21951                     colspan: '7',
21952                     cls: '',
21953                     cn: [
21954                         {
21955                             tag: 'button',
21956                             cls: 'btn btn-info ok',
21957                             html: 'OK'
21958                         }
21959                     ]
21960                 }
21961
21962                 ]
21963             }
21964         ]
21965     }
21966 });
21967
21968 Roo.apply(Roo.bootstrap.TimeField,  {
21969   
21970     template : {
21971         tag: 'div',
21972         cls: 'datepicker dropdown-menu',
21973         cn: [
21974             {
21975                 tag: 'div',
21976                 cls: 'datepicker-time',
21977                 cn: [
21978                 {
21979                     tag: 'table',
21980                     cls: 'table-condensed',
21981                     cn:[
21982                     Roo.bootstrap.TimeField.content,
21983                     Roo.bootstrap.TimeField.footer
21984                     ]
21985                 }
21986                 ]
21987             }
21988         ]
21989     }
21990 });
21991
21992  
21993
21994  /*
21995  * - LGPL
21996  *
21997  * MonthField
21998  * 
21999  */
22000
22001 /**
22002  * @class Roo.bootstrap.MonthField
22003  * @extends Roo.bootstrap.Input
22004  * Bootstrap MonthField class
22005  * 
22006  * @cfg {String} language default en
22007  * 
22008  * @constructor
22009  * Create a new MonthField
22010  * @param {Object} config The config object
22011  */
22012
22013 Roo.bootstrap.MonthField = function(config){
22014     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22015     
22016     this.addEvents({
22017         /**
22018          * @event show
22019          * Fires when this field show.
22020          * @param {Roo.bootstrap.MonthField} this
22021          * @param {Mixed} date The date value
22022          */
22023         show : true,
22024         /**
22025          * @event show
22026          * Fires when this field hide.
22027          * @param {Roo.bootstrap.MonthField} this
22028          * @param {Mixed} date The date value
22029          */
22030         hide : true,
22031         /**
22032          * @event select
22033          * Fires when select a date.
22034          * @param {Roo.bootstrap.MonthField} this
22035          * @param {String} oldvalue The old value
22036          * @param {String} newvalue The new value
22037          */
22038         select : true
22039     });
22040 };
22041
22042 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22043     
22044     onRender: function(ct, position)
22045     {
22046         
22047         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22048         
22049         this.language = this.language || 'en';
22050         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22051         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22052         
22053         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22054         this.isInline = false;
22055         this.isInput = true;
22056         this.component = this.el.select('.add-on', true).first() || false;
22057         this.component = (this.component && this.component.length === 0) ? false : this.component;
22058         this.hasInput = this.component && this.inputEL().length;
22059         
22060         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22061         
22062         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22063         
22064         this.picker().on('mousedown', this.onMousedown, this);
22065         this.picker().on('click', this.onClick, this);
22066         
22067         this.picker().addClass('datepicker-dropdown');
22068         
22069         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22070             v.setStyle('width', '189px');
22071         });
22072         
22073         this.fillMonths();
22074         
22075         this.update();
22076         
22077         if(this.isInline) {
22078             this.show();
22079         }
22080         
22081     },
22082     
22083     setValue: function(v, suppressEvent)
22084     {   
22085         var o = this.getValue();
22086         
22087         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22088         
22089         this.update();
22090
22091         if(suppressEvent !== true){
22092             this.fireEvent('select', this, o, v);
22093         }
22094         
22095     },
22096     
22097     getValue: function()
22098     {
22099         return this.value;
22100     },
22101     
22102     onClick: function(e) 
22103     {
22104         e.stopPropagation();
22105         e.preventDefault();
22106         
22107         var target = e.getTarget();
22108         
22109         if(target.nodeName.toLowerCase() === 'i'){
22110             target = Roo.get(target).dom.parentNode;
22111         }
22112         
22113         var nodeName = target.nodeName;
22114         var className = target.className;
22115         var html = target.innerHTML;
22116         
22117         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22118             return;
22119         }
22120         
22121         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22122         
22123         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22124         
22125         this.hide();
22126                         
22127     },
22128     
22129     picker : function()
22130     {
22131         return this.pickerEl;
22132     },
22133     
22134     fillMonths: function()
22135     {    
22136         var i = 0;
22137         var months = this.picker().select('>.datepicker-months td', true).first();
22138         
22139         months.dom.innerHTML = '';
22140         
22141         while (i < 12) {
22142             var month = {
22143                 tag: 'span',
22144                 cls: 'month',
22145                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22146             };
22147             
22148             months.createChild(month);
22149         }
22150         
22151     },
22152     
22153     update: function()
22154     {
22155         var _this = this;
22156         
22157         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22158             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22159         }
22160         
22161         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22162             e.removeClass('active');
22163             
22164             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22165                 e.addClass('active');
22166             }
22167         })
22168     },
22169     
22170     place: function()
22171     {
22172         if(this.isInline) {
22173             return;
22174         }
22175         
22176         this.picker().removeClass(['bottom', 'top']);
22177         
22178         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22179             /*
22180              * place to the top of element!
22181              *
22182              */
22183             
22184             this.picker().addClass('top');
22185             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22186             
22187             return;
22188         }
22189         
22190         this.picker().addClass('bottom');
22191         
22192         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22193     },
22194     
22195     onFocus : function()
22196     {
22197         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22198         this.show();
22199     },
22200     
22201     onBlur : function()
22202     {
22203         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22204         
22205         var d = this.inputEl().getValue();
22206         
22207         this.setValue(d);
22208                 
22209         this.hide();
22210     },
22211     
22212     show : function()
22213     {
22214         this.picker().show();
22215         this.picker().select('>.datepicker-months', true).first().show();
22216         this.update();
22217         this.place();
22218         
22219         this.fireEvent('show', this, this.date);
22220     },
22221     
22222     hide : function()
22223     {
22224         if(this.isInline) {
22225             return;
22226         }
22227         this.picker().hide();
22228         this.fireEvent('hide', this, this.date);
22229         
22230     },
22231     
22232     onMousedown: function(e)
22233     {
22234         e.stopPropagation();
22235         e.preventDefault();
22236     },
22237     
22238     keyup: function(e)
22239     {
22240         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22241         this.update();
22242     },
22243
22244     fireKey: function(e)
22245     {
22246         if (!this.picker().isVisible()){
22247             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22248                 this.show();
22249             }
22250             return;
22251         }
22252         
22253         var dir;
22254         
22255         switch(e.keyCode){
22256             case 27: // escape
22257                 this.hide();
22258                 e.preventDefault();
22259                 break;
22260             case 37: // left
22261             case 39: // right
22262                 dir = e.keyCode == 37 ? -1 : 1;
22263                 
22264                 this.vIndex = this.vIndex + dir;
22265                 
22266                 if(this.vIndex < 0){
22267                     this.vIndex = 0;
22268                 }
22269                 
22270                 if(this.vIndex > 11){
22271                     this.vIndex = 11;
22272                 }
22273                 
22274                 if(isNaN(this.vIndex)){
22275                     this.vIndex = 0;
22276                 }
22277                 
22278                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22279                 
22280                 break;
22281             case 38: // up
22282             case 40: // down
22283                 
22284                 dir = e.keyCode == 38 ? -1 : 1;
22285                 
22286                 this.vIndex = this.vIndex + dir * 4;
22287                 
22288                 if(this.vIndex < 0){
22289                     this.vIndex = 0;
22290                 }
22291                 
22292                 if(this.vIndex > 11){
22293                     this.vIndex = 11;
22294                 }
22295                 
22296                 if(isNaN(this.vIndex)){
22297                     this.vIndex = 0;
22298                 }
22299                 
22300                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22301                 break;
22302                 
22303             case 13: // enter
22304                 
22305                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22306                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22307                 }
22308                 
22309                 this.hide();
22310                 e.preventDefault();
22311                 break;
22312             case 9: // tab
22313                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22314                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22315                 }
22316                 this.hide();
22317                 break;
22318             case 16: // shift
22319             case 17: // ctrl
22320             case 18: // alt
22321                 break;
22322             default :
22323                 this.hide();
22324                 
22325         }
22326     },
22327     
22328     remove: function() 
22329     {
22330         this.picker().remove();
22331     }
22332    
22333 });
22334
22335 Roo.apply(Roo.bootstrap.MonthField,  {
22336     
22337     content : {
22338         tag: 'tbody',
22339         cn: [
22340         {
22341             tag: 'tr',
22342             cn: [
22343             {
22344                 tag: 'td',
22345                 colspan: '7'
22346             }
22347             ]
22348         }
22349         ]
22350     },
22351     
22352     dates:{
22353         en: {
22354             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22355             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22356         }
22357     }
22358 });
22359
22360 Roo.apply(Roo.bootstrap.MonthField,  {
22361   
22362     template : {
22363         tag: 'div',
22364         cls: 'datepicker dropdown-menu roo-dynamic',
22365         cn: [
22366             {
22367                 tag: 'div',
22368                 cls: 'datepicker-months',
22369                 cn: [
22370                 {
22371                     tag: 'table',
22372                     cls: 'table-condensed',
22373                     cn:[
22374                         Roo.bootstrap.DateField.content
22375                     ]
22376                 }
22377                 ]
22378             }
22379         ]
22380     }
22381 });
22382
22383  
22384
22385  
22386  /*
22387  * - LGPL
22388  *
22389  * CheckBox
22390  * 
22391  */
22392
22393 /**
22394  * @class Roo.bootstrap.CheckBox
22395  * @extends Roo.bootstrap.Input
22396  * Bootstrap CheckBox class
22397  * 
22398  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22399  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22400  * @cfg {String} boxLabel The text that appears beside the checkbox
22401  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22402  * @cfg {Boolean} checked initnal the element
22403  * @cfg {Boolean} inline inline the element (default false)
22404  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22405  * @cfg {String} tooltip label tooltip
22406  * 
22407  * @constructor
22408  * Create a new CheckBox
22409  * @param {Object} config The config object
22410  */
22411
22412 Roo.bootstrap.CheckBox = function(config){
22413     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22414    
22415     this.addEvents({
22416         /**
22417         * @event check
22418         * Fires when the element is checked or unchecked.
22419         * @param {Roo.bootstrap.CheckBox} this This input
22420         * @param {Boolean} checked The new checked value
22421         */
22422        check : true,
22423        /**
22424         * @event click
22425         * Fires when the element is click.
22426         * @param {Roo.bootstrap.CheckBox} this This input
22427         */
22428        click : true
22429     });
22430     
22431 };
22432
22433 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22434   
22435     inputType: 'checkbox',
22436     inputValue: 1,
22437     valueOff: 0,
22438     boxLabel: false,
22439     checked: false,
22440     weight : false,
22441     inline: false,
22442     tooltip : '',
22443     
22444     // checkbox success does not make any sense really.. 
22445     invalidClass : "",
22446     validClass : "",
22447     
22448     
22449     getAutoCreate : function()
22450     {
22451         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22452         
22453         var id = Roo.id();
22454         
22455         var cfg = {};
22456         
22457         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22458         
22459         if(this.inline){
22460             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22461         }
22462         
22463         var input =  {
22464             tag: 'input',
22465             id : id,
22466             type : this.inputType,
22467             value : this.inputValue,
22468             cls : 'roo-' + this.inputType, //'form-box',
22469             placeholder : this.placeholder || ''
22470             
22471         };
22472         
22473         if(this.inputType != 'radio'){
22474             var hidden =  {
22475                 tag: 'input',
22476                 type : 'hidden',
22477                 cls : 'roo-hidden-value',
22478                 value : this.checked ? this.inputValue : this.valueOff
22479             };
22480         }
22481         
22482             
22483         if (this.weight) { // Validity check?
22484             cfg.cls += " " + this.inputType + "-" + this.weight;
22485         }
22486         
22487         if (this.disabled) {
22488             input.disabled=true;
22489         }
22490         
22491         if(this.checked){
22492             input.checked = this.checked;
22493         }
22494         
22495         if (this.name) {
22496             
22497             input.name = this.name;
22498             
22499             if(this.inputType != 'radio'){
22500                 hidden.name = this.name;
22501                 input.name = '_hidden_' + this.name;
22502             }
22503         }
22504         
22505         if (this.size) {
22506             input.cls += ' input-' + this.size;
22507         }
22508         
22509         var settings=this;
22510         
22511         ['xs','sm','md','lg'].map(function(size){
22512             if (settings[size]) {
22513                 cfg.cls += ' col-' + size + '-' + settings[size];
22514             }
22515         });
22516         
22517         var inputblock = input;
22518          
22519         if (this.before || this.after) {
22520             
22521             inputblock = {
22522                 cls : 'input-group',
22523                 cn :  [] 
22524             };
22525             
22526             if (this.before) {
22527                 inputblock.cn.push({
22528                     tag :'span',
22529                     cls : 'input-group-addon',
22530                     html : this.before
22531                 });
22532             }
22533             
22534             inputblock.cn.push(input);
22535             
22536             if(this.inputType != 'radio'){
22537                 inputblock.cn.push(hidden);
22538             }
22539             
22540             if (this.after) {
22541                 inputblock.cn.push({
22542                     tag :'span',
22543                     cls : 'input-group-addon',
22544                     html : this.after
22545                 });
22546             }
22547             
22548         }
22549         var boxLabelCfg = false;
22550         
22551         if(this.boxLabel){
22552            
22553             boxLabelCfg = {
22554                 tag: 'label',
22555                 //'for': id, // box label is handled by onclick - so no for...
22556                 cls: 'box-label',
22557                 html: this.boxLabel
22558             };
22559             if(this.tooltip){
22560                 boxLabelCfg.tooltip = this.tooltip;
22561             }
22562              
22563         }
22564         
22565         
22566         if (align ==='left' && this.fieldLabel.length) {
22567 //                Roo.log("left and has label");
22568             cfg.cn = [
22569                 {
22570                     tag: 'label',
22571                     'for' :  id,
22572                     cls : 'control-label',
22573                     html : this.fieldLabel
22574                 },
22575                 {
22576                     cls : "", 
22577                     cn: [
22578                         inputblock
22579                     ]
22580                 }
22581             ];
22582             
22583             if (boxLabelCfg) {
22584                 cfg.cn[1].cn.push(boxLabelCfg);
22585             }
22586             
22587             if(this.labelWidth > 12){
22588                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22589             }
22590             
22591             if(this.labelWidth < 13 && this.labelmd == 0){
22592                 this.labelmd = this.labelWidth;
22593             }
22594             
22595             if(this.labellg > 0){
22596                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22597                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22598             }
22599             
22600             if(this.labelmd > 0){
22601                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22602                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22603             }
22604             
22605             if(this.labelsm > 0){
22606                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22607                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22608             }
22609             
22610             if(this.labelxs > 0){
22611                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22612                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22613             }
22614             
22615         } else if ( this.fieldLabel.length) {
22616 //                Roo.log(" label");
22617                 cfg.cn = [
22618                    
22619                     {
22620                         tag: this.boxLabel ? 'span' : 'label',
22621                         'for': id,
22622                         cls: 'control-label box-input-label',
22623                         //cls : 'input-group-addon',
22624                         html : this.fieldLabel
22625                     },
22626                     
22627                     inputblock
22628                     
22629                 ];
22630                 if (boxLabelCfg) {
22631                     cfg.cn.push(boxLabelCfg);
22632                 }
22633
22634         } else {
22635             
22636 //                Roo.log(" no label && no align");
22637                 cfg.cn = [  inputblock ] ;
22638                 if (boxLabelCfg) {
22639                     cfg.cn.push(boxLabelCfg);
22640                 }
22641
22642                 
22643         }
22644         
22645        
22646         
22647         if(this.inputType != 'radio'){
22648             cfg.cn.push(hidden);
22649         }
22650         
22651         return cfg;
22652         
22653     },
22654     
22655     /**
22656      * return the real input element.
22657      */
22658     inputEl: function ()
22659     {
22660         return this.el.select('input.roo-' + this.inputType,true).first();
22661     },
22662     hiddenEl: function ()
22663     {
22664         return this.el.select('input.roo-hidden-value',true).first();
22665     },
22666     
22667     labelEl: function()
22668     {
22669         return this.el.select('label.control-label',true).first();
22670     },
22671     /* depricated... */
22672     
22673     label: function()
22674     {
22675         return this.labelEl();
22676     },
22677     
22678     boxLabelEl: function()
22679     {
22680         return this.el.select('label.box-label',true).first();
22681     },
22682     
22683     initEvents : function()
22684     {
22685 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22686         
22687         this.inputEl().on('click', this.onClick,  this);
22688         
22689         if (this.boxLabel) { 
22690             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
22691         }
22692         
22693         this.startValue = this.getValue();
22694         
22695         if(this.groupId){
22696             Roo.bootstrap.CheckBox.register(this);
22697         }
22698     },
22699     
22700     onClick : function(e)
22701     {   
22702         if(this.fireEvent('click', this, e) !== false){
22703             this.setChecked(!this.checked);
22704         }
22705         
22706     },
22707     
22708     setChecked : function(state,suppressEvent)
22709     {
22710         this.startValue = this.getValue();
22711
22712         if(this.inputType == 'radio'){
22713             
22714             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22715                 e.dom.checked = false;
22716             });
22717             
22718             this.inputEl().dom.checked = true;
22719             
22720             this.inputEl().dom.value = this.inputValue;
22721             
22722             if(suppressEvent !== true){
22723                 this.fireEvent('check', this, true);
22724             }
22725             
22726             this.validate();
22727             
22728             return;
22729         }
22730         
22731         this.checked = state;
22732         
22733         this.inputEl().dom.checked = state;
22734         
22735         
22736         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22737         
22738         if(suppressEvent !== true){
22739             this.fireEvent('check', this, state);
22740         }
22741         
22742         this.validate();
22743     },
22744     
22745     getValue : function()
22746     {
22747         if(this.inputType == 'radio'){
22748             return this.getGroupValue();
22749         }
22750         
22751         return this.hiddenEl().dom.value;
22752         
22753     },
22754     
22755     getGroupValue : function()
22756     {
22757         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22758             return '';
22759         }
22760         
22761         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22762     },
22763     
22764     setValue : function(v,suppressEvent)
22765     {
22766         if(this.inputType == 'radio'){
22767             this.setGroupValue(v, suppressEvent);
22768             return;
22769         }
22770         
22771         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22772         
22773         this.validate();
22774     },
22775     
22776     setGroupValue : function(v, suppressEvent)
22777     {
22778         this.startValue = this.getValue();
22779         
22780         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22781             e.dom.checked = false;
22782             
22783             if(e.dom.value == v){
22784                 e.dom.checked = true;
22785             }
22786         });
22787         
22788         if(suppressEvent !== true){
22789             this.fireEvent('check', this, true);
22790         }
22791
22792         this.validate();
22793         
22794         return;
22795     },
22796     
22797     validate : function()
22798     {
22799         if(this.getVisibilityEl().hasClass('hidden')){
22800             return true;
22801         }
22802         
22803         if(
22804                 this.disabled || 
22805                 (this.inputType == 'radio' && this.validateRadio()) ||
22806                 (this.inputType == 'checkbox' && this.validateCheckbox())
22807         ){
22808             this.markValid();
22809             return true;
22810         }
22811         
22812         this.markInvalid();
22813         return false;
22814     },
22815     
22816     validateRadio : function()
22817     {
22818         if(this.getVisibilityEl().hasClass('hidden')){
22819             return true;
22820         }
22821         
22822         if(this.allowBlank){
22823             return true;
22824         }
22825         
22826         var valid = false;
22827         
22828         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22829             if(!e.dom.checked){
22830                 return;
22831             }
22832             
22833             valid = true;
22834             
22835             return false;
22836         });
22837         
22838         return valid;
22839     },
22840     
22841     validateCheckbox : function()
22842     {
22843         if(!this.groupId){
22844             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22845             //return (this.getValue() == this.inputValue) ? true : false;
22846         }
22847         
22848         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22849         
22850         if(!group){
22851             return false;
22852         }
22853         
22854         var r = false;
22855         
22856         for(var i in group){
22857             if(group[i].el.isVisible(true)){
22858                 r = false;
22859                 break;
22860             }
22861             
22862             r = true;
22863         }
22864         
22865         for(var i in group){
22866             if(r){
22867                 break;
22868             }
22869             
22870             r = (group[i].getValue() == group[i].inputValue) ? true : false;
22871         }
22872         
22873         return r;
22874     },
22875     
22876     /**
22877      * Mark this field as valid
22878      */
22879     markValid : function()
22880     {
22881         var _this = this;
22882         
22883         this.fireEvent('valid', this);
22884         
22885         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22886         
22887         if(this.groupId){
22888             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22889         }
22890         
22891         if(label){
22892             label.markValid();
22893         }
22894
22895         if(this.inputType == 'radio'){
22896             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22897                 var fg = e.findParent('.form-group', false, true);
22898                 if (Roo.bootstrap.version == 3) {
22899                     fg.removeClass([_this.invalidClass, _this.validClass]);
22900                     fg.addClass(_this.validClass);
22901                 } else {
22902                     fg.removeClass(['is-valid', 'is-invalid']);
22903                     fg.addClass('is-valid');
22904                 }
22905             });
22906             
22907             return;
22908         }
22909
22910         if(!this.groupId){
22911             var fg = this.el.findParent('.form-group', false, true);
22912             if (Roo.bootstrap.version == 3) {
22913                 fg.removeClass([this.invalidClass, this.validClass]);
22914                 fg.addClass(this.validClass);
22915             } else {
22916                 fg.removeClass(['is-valid', 'is-invalid']);
22917                 fg.addClass('is-valid');
22918             }
22919             return;
22920         }
22921         
22922         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22923         
22924         if(!group){
22925             return;
22926         }
22927         
22928         for(var i in group){
22929             var fg = group[i].el.findParent('.form-group', false, true);
22930             if (Roo.bootstrap.version == 3) {
22931                 fg.removeClass([this.invalidClass, this.validClass]);
22932                 fg.addClass(this.validClass);
22933             } else {
22934                 fg.removeClass(['is-valid', 'is-invalid']);
22935                 fg.addClass('is-valid');
22936             }
22937         }
22938     },
22939     
22940      /**
22941      * Mark this field as invalid
22942      * @param {String} msg The validation message
22943      */
22944     markInvalid : function(msg)
22945     {
22946         if(this.allowBlank){
22947             return;
22948         }
22949         
22950         var _this = this;
22951         
22952         this.fireEvent('invalid', this, msg);
22953         
22954         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22955         
22956         if(this.groupId){
22957             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22958         }
22959         
22960         if(label){
22961             label.markInvalid();
22962         }
22963             
22964         if(this.inputType == 'radio'){
22965             
22966             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22967                 var fg = e.findParent('.form-group', false, true);
22968                 if (Roo.bootstrap.version == 3) {
22969                     fg.removeClass([_this.invalidClass, _this.validClass]);
22970                     fg.addClass(_this.invalidClass);
22971                 } else {
22972                     fg.removeClass(['is-invalid', 'is-valid']);
22973                     fg.addClass('is-invalid');
22974                 }
22975             });
22976             
22977             return;
22978         }
22979         
22980         if(!this.groupId){
22981             var fg = this.el.findParent('.form-group', false, true);
22982             if (Roo.bootstrap.version == 3) {
22983                 fg.removeClass([_this.invalidClass, _this.validClass]);
22984                 fg.addClass(_this.invalidClass);
22985             } else {
22986                 fg.removeClass(['is-invalid', 'is-valid']);
22987                 fg.addClass('is-invalid');
22988             }
22989             return;
22990         }
22991         
22992         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22993         
22994         if(!group){
22995             return;
22996         }
22997         
22998         for(var i in group){
22999             var fg = group[i].el.findParent('.form-group', false, true);
23000             if (Roo.bootstrap.version == 3) {
23001                 fg.removeClass([_this.invalidClass, _this.validClass]);
23002                 fg.addClass(_this.invalidClass);
23003             } else {
23004                 fg.removeClass(['is-invalid', 'is-valid']);
23005                 fg.addClass('is-invalid');
23006             }
23007         }
23008         
23009     },
23010     
23011     clearInvalid : function()
23012     {
23013         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23014         
23015         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23016         
23017         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23018         
23019         if (label && label.iconEl) {
23020             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23021             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23022         }
23023     },
23024     
23025     disable : function()
23026     {
23027         if(this.inputType != 'radio'){
23028             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23029             return;
23030         }
23031         
23032         var _this = this;
23033         
23034         if(this.rendered){
23035             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23036                 _this.getActionEl().addClass(this.disabledClass);
23037                 e.dom.disabled = true;
23038             });
23039         }
23040         
23041         this.disabled = true;
23042         this.fireEvent("disable", this);
23043         return this;
23044     },
23045
23046     enable : function()
23047     {
23048         if(this.inputType != 'radio'){
23049             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23050             return;
23051         }
23052         
23053         var _this = this;
23054         
23055         if(this.rendered){
23056             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23057                 _this.getActionEl().removeClass(this.disabledClass);
23058                 e.dom.disabled = false;
23059             });
23060         }
23061         
23062         this.disabled = false;
23063         this.fireEvent("enable", this);
23064         return this;
23065     },
23066     
23067     setBoxLabel : function(v)
23068     {
23069         this.boxLabel = v;
23070         
23071         if(this.rendered){
23072             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23073         }
23074     }
23075
23076 });
23077
23078 Roo.apply(Roo.bootstrap.CheckBox, {
23079     
23080     groups: {},
23081     
23082      /**
23083     * register a CheckBox Group
23084     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23085     */
23086     register : function(checkbox)
23087     {
23088         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23089             this.groups[checkbox.groupId] = {};
23090         }
23091         
23092         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23093             return;
23094         }
23095         
23096         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23097         
23098     },
23099     /**
23100     * fetch a CheckBox Group based on the group ID
23101     * @param {string} the group ID
23102     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23103     */
23104     get: function(groupId) {
23105         if (typeof(this.groups[groupId]) == 'undefined') {
23106             return false;
23107         }
23108         
23109         return this.groups[groupId] ;
23110     }
23111     
23112     
23113 });
23114 /*
23115  * - LGPL
23116  *
23117  * RadioItem
23118  * 
23119  */
23120
23121 /**
23122  * @class Roo.bootstrap.Radio
23123  * @extends Roo.bootstrap.Component
23124  * Bootstrap Radio class
23125  * @cfg {String} boxLabel - the label associated
23126  * @cfg {String} value - the value of radio
23127  * 
23128  * @constructor
23129  * Create a new Radio
23130  * @param {Object} config The config object
23131  */
23132 Roo.bootstrap.Radio = function(config){
23133     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23134     
23135 };
23136
23137 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23138     
23139     boxLabel : '',
23140     
23141     value : '',
23142     
23143     getAutoCreate : function()
23144     {
23145         var cfg = {
23146             tag : 'div',
23147             cls : 'form-group radio',
23148             cn : [
23149                 {
23150                     tag : 'label',
23151                     cls : 'box-label',
23152                     html : this.boxLabel
23153                 }
23154             ]
23155         };
23156         
23157         return cfg;
23158     },
23159     
23160     initEvents : function() 
23161     {
23162         this.parent().register(this);
23163         
23164         this.el.on('click', this.onClick, this);
23165         
23166     },
23167     
23168     onClick : function(e)
23169     {
23170         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23171             this.setChecked(true);
23172         }
23173     },
23174     
23175     setChecked : function(state, suppressEvent)
23176     {
23177         this.parent().setValue(this.value, suppressEvent);
23178         
23179     },
23180     
23181     setBoxLabel : function(v)
23182     {
23183         this.boxLabel = v;
23184         
23185         if(this.rendered){
23186             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23187         }
23188     }
23189     
23190 });
23191  
23192
23193  /*
23194  * - LGPL
23195  *
23196  * Input
23197  * 
23198  */
23199
23200 /**
23201  * @class Roo.bootstrap.SecurePass
23202  * @extends Roo.bootstrap.Input
23203  * Bootstrap SecurePass class
23204  *
23205  * 
23206  * @constructor
23207  * Create a new SecurePass
23208  * @param {Object} config The config object
23209  */
23210  
23211 Roo.bootstrap.SecurePass = function (config) {
23212     // these go here, so the translation tool can replace them..
23213     this.errors = {
23214         PwdEmpty: "Please type a password, and then retype it to confirm.",
23215         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23216         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23217         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23218         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23219         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23220         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23221         TooWeak: "Your password is Too Weak."
23222     },
23223     this.meterLabel = "Password strength:";
23224     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23225     this.meterClass = [
23226         "roo-password-meter-tooweak", 
23227         "roo-password-meter-weak", 
23228         "roo-password-meter-medium", 
23229         "roo-password-meter-strong", 
23230         "roo-password-meter-grey"
23231     ];
23232     
23233     this.errors = {};
23234     
23235     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23236 }
23237
23238 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23239     /**
23240      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23241      * {
23242      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23243      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23244      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23245      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23246      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23247      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23248      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23249      * })
23250      */
23251     // private
23252     
23253     meterWidth: 300,
23254     errorMsg :'',    
23255     errors: false,
23256     imageRoot: '/',
23257     /**
23258      * @cfg {String/Object} Label for the strength meter (defaults to
23259      * 'Password strength:')
23260      */
23261     // private
23262     meterLabel: '',
23263     /**
23264      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23265      * ['Weak', 'Medium', 'Strong'])
23266      */
23267     // private    
23268     pwdStrengths: false,    
23269     // private
23270     strength: 0,
23271     // private
23272     _lastPwd: null,
23273     // private
23274     kCapitalLetter: 0,
23275     kSmallLetter: 1,
23276     kDigit: 2,
23277     kPunctuation: 3,
23278     
23279     insecure: false,
23280     // private
23281     initEvents: function ()
23282     {
23283         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23284
23285         if (this.el.is('input[type=password]') && Roo.isSafari) {
23286             this.el.on('keydown', this.SafariOnKeyDown, this);
23287         }
23288
23289         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23290     },
23291     // private
23292     onRender: function (ct, position)
23293     {
23294         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23295         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23296         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23297
23298         this.trigger.createChild({
23299                    cn: [
23300                     {
23301                     //id: 'PwdMeter',
23302                     tag: 'div',
23303                     cls: 'roo-password-meter-grey col-xs-12',
23304                     style: {
23305                         //width: 0,
23306                         //width: this.meterWidth + 'px'                                                
23307                         }
23308                     },
23309                     {                            
23310                          cls: 'roo-password-meter-text'                          
23311                     }
23312                 ]            
23313         });
23314
23315          
23316         if (this.hideTrigger) {
23317             this.trigger.setDisplayed(false);
23318         }
23319         this.setSize(this.width || '', this.height || '');
23320     },
23321     // private
23322     onDestroy: function ()
23323     {
23324         if (this.trigger) {
23325             this.trigger.removeAllListeners();
23326             this.trigger.remove();
23327         }
23328         if (this.wrap) {
23329             this.wrap.remove();
23330         }
23331         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23332     },
23333     // private
23334     checkStrength: function ()
23335     {
23336         var pwd = this.inputEl().getValue();
23337         if (pwd == this._lastPwd) {
23338             return;
23339         }
23340
23341         var strength;
23342         if (this.ClientSideStrongPassword(pwd)) {
23343             strength = 3;
23344         } else if (this.ClientSideMediumPassword(pwd)) {
23345             strength = 2;
23346         } else if (this.ClientSideWeakPassword(pwd)) {
23347             strength = 1;
23348         } else {
23349             strength = 0;
23350         }
23351         
23352         Roo.log('strength1: ' + strength);
23353         
23354         //var pm = this.trigger.child('div/div/div').dom;
23355         var pm = this.trigger.child('div/div');
23356         pm.removeClass(this.meterClass);
23357         pm.addClass(this.meterClass[strength]);
23358                 
23359         
23360         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23361                 
23362         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23363         
23364         this._lastPwd = pwd;
23365     },
23366     reset: function ()
23367     {
23368         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23369         
23370         this._lastPwd = '';
23371         
23372         var pm = this.trigger.child('div/div');
23373         pm.removeClass(this.meterClass);
23374         pm.addClass('roo-password-meter-grey');        
23375         
23376         
23377         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23378         
23379         pt.innerHTML = '';
23380         this.inputEl().dom.type='password';
23381     },
23382     // private
23383     validateValue: function (value)
23384     {
23385         
23386         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23387             return false;
23388         }
23389         if (value.length == 0) {
23390             if (this.allowBlank) {
23391                 this.clearInvalid();
23392                 return true;
23393             }
23394
23395             this.markInvalid(this.errors.PwdEmpty);
23396             this.errorMsg = this.errors.PwdEmpty;
23397             return false;
23398         }
23399         
23400         if(this.insecure){
23401             return true;
23402         }
23403         
23404         if ('[\x21-\x7e]*'.match(value)) {
23405             this.markInvalid(this.errors.PwdBadChar);
23406             this.errorMsg = this.errors.PwdBadChar;
23407             return false;
23408         }
23409         if (value.length < 6) {
23410             this.markInvalid(this.errors.PwdShort);
23411             this.errorMsg = this.errors.PwdShort;
23412             return false;
23413         }
23414         if (value.length > 16) {
23415             this.markInvalid(this.errors.PwdLong);
23416             this.errorMsg = this.errors.PwdLong;
23417             return false;
23418         }
23419         var strength;
23420         if (this.ClientSideStrongPassword(value)) {
23421             strength = 3;
23422         } else if (this.ClientSideMediumPassword(value)) {
23423             strength = 2;
23424         } else if (this.ClientSideWeakPassword(value)) {
23425             strength = 1;
23426         } else {
23427             strength = 0;
23428         }
23429
23430         
23431         if (strength < 2) {
23432             //this.markInvalid(this.errors.TooWeak);
23433             this.errorMsg = this.errors.TooWeak;
23434             //return false;
23435         }
23436         
23437         
23438         console.log('strength2: ' + strength);
23439         
23440         //var pm = this.trigger.child('div/div/div').dom;
23441         
23442         var pm = this.trigger.child('div/div');
23443         pm.removeClass(this.meterClass);
23444         pm.addClass(this.meterClass[strength]);
23445                 
23446         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23447                 
23448         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23449         
23450         this.errorMsg = ''; 
23451         return true;
23452     },
23453     // private
23454     CharacterSetChecks: function (type)
23455     {
23456         this.type = type;
23457         this.fResult = false;
23458     },
23459     // private
23460     isctype: function (character, type)
23461     {
23462         switch (type) {  
23463             case this.kCapitalLetter:
23464                 if (character >= 'A' && character <= 'Z') {
23465                     return true;
23466                 }
23467                 break;
23468             
23469             case this.kSmallLetter:
23470                 if (character >= 'a' && character <= 'z') {
23471                     return true;
23472                 }
23473                 break;
23474             
23475             case this.kDigit:
23476                 if (character >= '0' && character <= '9') {
23477                     return true;
23478                 }
23479                 break;
23480             
23481             case this.kPunctuation:
23482                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23483                     return true;
23484                 }
23485                 break;
23486             
23487             default:
23488                 return false;
23489         }
23490
23491     },
23492     // private
23493     IsLongEnough: function (pwd, size)
23494     {
23495         return !(pwd == null || isNaN(size) || pwd.length < size);
23496     },
23497     // private
23498     SpansEnoughCharacterSets: function (word, nb)
23499     {
23500         if (!this.IsLongEnough(word, nb))
23501         {
23502             return false;
23503         }
23504
23505         var characterSetChecks = new Array(
23506             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23507             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23508         );
23509         
23510         for (var index = 0; index < word.length; ++index) {
23511             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23512                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23513                     characterSetChecks[nCharSet].fResult = true;
23514                     break;
23515                 }
23516             }
23517         }
23518
23519         var nCharSets = 0;
23520         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23521             if (characterSetChecks[nCharSet].fResult) {
23522                 ++nCharSets;
23523             }
23524         }
23525
23526         if (nCharSets < nb) {
23527             return false;
23528         }
23529         return true;
23530     },
23531     // private
23532     ClientSideStrongPassword: function (pwd)
23533     {
23534         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23535     },
23536     // private
23537     ClientSideMediumPassword: function (pwd)
23538     {
23539         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23540     },
23541     // private
23542     ClientSideWeakPassword: function (pwd)
23543     {
23544         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23545     }
23546           
23547 })//<script type="text/javascript">
23548
23549 /*
23550  * Based  Ext JS Library 1.1.1
23551  * Copyright(c) 2006-2007, Ext JS, LLC.
23552  * LGPL
23553  *
23554  */
23555  
23556 /**
23557  * @class Roo.HtmlEditorCore
23558  * @extends Roo.Component
23559  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23560  *
23561  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23562  */
23563
23564 Roo.HtmlEditorCore = function(config){
23565     
23566     
23567     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23568     
23569     
23570     this.addEvents({
23571         /**
23572          * @event initialize
23573          * Fires when the editor is fully initialized (including the iframe)
23574          * @param {Roo.HtmlEditorCore} this
23575          */
23576         initialize: true,
23577         /**
23578          * @event activate
23579          * Fires when the editor is first receives the focus. Any insertion must wait
23580          * until after this event.
23581          * @param {Roo.HtmlEditorCore} this
23582          */
23583         activate: true,
23584          /**
23585          * @event beforesync
23586          * Fires before the textarea is updated with content from the editor iframe. Return false
23587          * to cancel the sync.
23588          * @param {Roo.HtmlEditorCore} this
23589          * @param {String} html
23590          */
23591         beforesync: true,
23592          /**
23593          * @event beforepush
23594          * Fires before the iframe editor is updated with content from the textarea. Return false
23595          * to cancel the push.
23596          * @param {Roo.HtmlEditorCore} this
23597          * @param {String} html
23598          */
23599         beforepush: true,
23600          /**
23601          * @event sync
23602          * Fires when the textarea is updated with content from the editor iframe.
23603          * @param {Roo.HtmlEditorCore} this
23604          * @param {String} html
23605          */
23606         sync: true,
23607          /**
23608          * @event push
23609          * Fires when the iframe editor is updated with content from the textarea.
23610          * @param {Roo.HtmlEditorCore} this
23611          * @param {String} html
23612          */
23613         push: true,
23614         
23615         /**
23616          * @event editorevent
23617          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23618          * @param {Roo.HtmlEditorCore} this
23619          */
23620         editorevent: true
23621         
23622     });
23623     
23624     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23625     
23626     // defaults : white / black...
23627     this.applyBlacklists();
23628     
23629     
23630     
23631 };
23632
23633
23634 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
23635
23636
23637      /**
23638      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
23639      */
23640     
23641     owner : false,
23642     
23643      /**
23644      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23645      *                        Roo.resizable.
23646      */
23647     resizable : false,
23648      /**
23649      * @cfg {Number} height (in pixels)
23650      */   
23651     height: 300,
23652    /**
23653      * @cfg {Number} width (in pixels)
23654      */   
23655     width: 500,
23656     
23657     /**
23658      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23659      * 
23660      */
23661     stylesheets: false,
23662     
23663     // id of frame..
23664     frameId: false,
23665     
23666     // private properties
23667     validationEvent : false,
23668     deferHeight: true,
23669     initialized : false,
23670     activated : false,
23671     sourceEditMode : false,
23672     onFocus : Roo.emptyFn,
23673     iframePad:3,
23674     hideMode:'offsets',
23675     
23676     clearUp: true,
23677     
23678     // blacklist + whitelisted elements..
23679     black: false,
23680     white: false,
23681      
23682     bodyCls : '',
23683
23684     /**
23685      * Protected method that will not generally be called directly. It
23686      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23687      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23688      */
23689     getDocMarkup : function(){
23690         // body styles..
23691         var st = '';
23692         
23693         // inherit styels from page...?? 
23694         if (this.stylesheets === false) {
23695             
23696             Roo.get(document.head).select('style').each(function(node) {
23697                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23698             });
23699             
23700             Roo.get(document.head).select('link').each(function(node) { 
23701                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23702             });
23703             
23704         } else if (!this.stylesheets.length) {
23705                 // simple..
23706                 st = '<style type="text/css">' +
23707                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23708                    '</style>';
23709         } else { 
23710             st = '<style type="text/css">' +
23711                     this.stylesheets +
23712                 '</style>';
23713         }
23714         
23715         st +=  '<style type="text/css">' +
23716             'IMG { cursor: pointer } ' +
23717         '</style>';
23718
23719         var cls = 'roo-htmleditor-body';
23720         
23721         if(this.bodyCls.length){
23722             cls += ' ' + this.bodyCls;
23723         }
23724         
23725         return '<html><head>' + st  +
23726             //<style type="text/css">' +
23727             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23728             //'</style>' +
23729             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
23730     },
23731
23732     // private
23733     onRender : function(ct, position)
23734     {
23735         var _t = this;
23736         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23737         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23738         
23739         
23740         this.el.dom.style.border = '0 none';
23741         this.el.dom.setAttribute('tabIndex', -1);
23742         this.el.addClass('x-hidden hide');
23743         
23744         
23745         
23746         if(Roo.isIE){ // fix IE 1px bogus margin
23747             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23748         }
23749        
23750         
23751         this.frameId = Roo.id();
23752         
23753          
23754         
23755         var iframe = this.owner.wrap.createChild({
23756             tag: 'iframe',
23757             cls: 'form-control', // bootstrap..
23758             id: this.frameId,
23759             name: this.frameId,
23760             frameBorder : 'no',
23761             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
23762         }, this.el
23763         );
23764         
23765         
23766         this.iframe = iframe.dom;
23767
23768          this.assignDocWin();
23769         
23770         this.doc.designMode = 'on';
23771        
23772         this.doc.open();
23773         this.doc.write(this.getDocMarkup());
23774         this.doc.close();
23775
23776         
23777         var task = { // must defer to wait for browser to be ready
23778             run : function(){
23779                 //console.log("run task?" + this.doc.readyState);
23780                 this.assignDocWin();
23781                 if(this.doc.body || this.doc.readyState == 'complete'){
23782                     try {
23783                         this.doc.designMode="on";
23784                     } catch (e) {
23785                         return;
23786                     }
23787                     Roo.TaskMgr.stop(task);
23788                     this.initEditor.defer(10, this);
23789                 }
23790             },
23791             interval : 10,
23792             duration: 10000,
23793             scope: this
23794         };
23795         Roo.TaskMgr.start(task);
23796
23797     },
23798
23799     // private
23800     onResize : function(w, h)
23801     {
23802          Roo.log('resize: ' +w + ',' + h );
23803         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23804         if(!this.iframe){
23805             return;
23806         }
23807         if(typeof w == 'number'){
23808             
23809             this.iframe.style.width = w + 'px';
23810         }
23811         if(typeof h == 'number'){
23812             
23813             this.iframe.style.height = h + 'px';
23814             if(this.doc){
23815                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23816             }
23817         }
23818         
23819     },
23820
23821     /**
23822      * Toggles the editor between standard and source edit mode.
23823      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23824      */
23825     toggleSourceEdit : function(sourceEditMode){
23826         
23827         this.sourceEditMode = sourceEditMode === true;
23828         
23829         if(this.sourceEditMode){
23830  
23831             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
23832             
23833         }else{
23834             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23835             //this.iframe.className = '';
23836             this.deferFocus();
23837         }
23838         //this.setSize(this.owner.wrap.getSize());
23839         //this.fireEvent('editmodechange', this, this.sourceEditMode);
23840     },
23841
23842     
23843   
23844
23845     /**
23846      * Protected method that will not generally be called directly. If you need/want
23847      * custom HTML cleanup, this is the method you should override.
23848      * @param {String} html The HTML to be cleaned
23849      * return {String} The cleaned HTML
23850      */
23851     cleanHtml : function(html){
23852         html = String(html);
23853         if(html.length > 5){
23854             if(Roo.isSafari){ // strip safari nonsense
23855                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23856             }
23857         }
23858         if(html == '&nbsp;'){
23859             html = '';
23860         }
23861         return html;
23862     },
23863
23864     /**
23865      * HTML Editor -> Textarea
23866      * Protected method that will not generally be called directly. Syncs the contents
23867      * of the editor iframe with the textarea.
23868      */
23869     syncValue : function(){
23870         if(this.initialized){
23871             var bd = (this.doc.body || this.doc.documentElement);
23872             //this.cleanUpPaste(); -- this is done else where and causes havoc..
23873             var html = bd.innerHTML;
23874             if(Roo.isSafari){
23875                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23876                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23877                 if(m && m[1]){
23878                     html = '<div style="'+m[0]+'">' + html + '</div>';
23879                 }
23880             }
23881             html = this.cleanHtml(html);
23882             // fix up the special chars.. normaly like back quotes in word...
23883             // however we do not want to do this with chinese..
23884             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23885                 
23886                 var cc = match.charCodeAt();
23887
23888                 // Get the character value, handling surrogate pairs
23889                 if (match.length == 2) {
23890                     // It's a surrogate pair, calculate the Unicode code point
23891                     var high = match.charCodeAt(0) - 0xD800;
23892                     var low  = match.charCodeAt(1) - 0xDC00;
23893                     cc = (high * 0x400) + low + 0x10000;
23894                 }  else if (
23895                     (cc >= 0x4E00 && cc < 0xA000 ) ||
23896                     (cc >= 0x3400 && cc < 0x4E00 ) ||
23897                     (cc >= 0xf900 && cc < 0xfb00 )
23898                 ) {
23899                         return match;
23900                 }  
23901          
23902                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
23903                 return "&#" + cc + ";";
23904                 
23905                 
23906             });
23907             
23908             
23909              
23910             if(this.owner.fireEvent('beforesync', this, html) !== false){
23911                 this.el.dom.value = html;
23912                 this.owner.fireEvent('sync', this, html);
23913             }
23914         }
23915     },
23916
23917     /**
23918      * Protected method that will not generally be called directly. Pushes the value of the textarea
23919      * into the iframe editor.
23920      */
23921     pushValue : function(){
23922         if(this.initialized){
23923             var v = this.el.dom.value.trim();
23924             
23925 //            if(v.length < 1){
23926 //                v = '&#160;';
23927 //            }
23928             
23929             if(this.owner.fireEvent('beforepush', this, v) !== false){
23930                 var d = (this.doc.body || this.doc.documentElement);
23931                 d.innerHTML = v;
23932                 this.cleanUpPaste();
23933                 this.el.dom.value = d.innerHTML;
23934                 this.owner.fireEvent('push', this, v);
23935             }
23936         }
23937     },
23938
23939     // private
23940     deferFocus : function(){
23941         this.focus.defer(10, this);
23942     },
23943
23944     // doc'ed in Field
23945     focus : function(){
23946         if(this.win && !this.sourceEditMode){
23947             this.win.focus();
23948         }else{
23949             this.el.focus();
23950         }
23951     },
23952     
23953     assignDocWin: function()
23954     {
23955         var iframe = this.iframe;
23956         
23957          if(Roo.isIE){
23958             this.doc = iframe.contentWindow.document;
23959             this.win = iframe.contentWindow;
23960         } else {
23961 //            if (!Roo.get(this.frameId)) {
23962 //                return;
23963 //            }
23964 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23965 //            this.win = Roo.get(this.frameId).dom.contentWindow;
23966             
23967             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
23968                 return;
23969             }
23970             
23971             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23972             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
23973         }
23974     },
23975     
23976     // private
23977     initEditor : function(){
23978         //console.log("INIT EDITOR");
23979         this.assignDocWin();
23980         
23981         
23982         
23983         this.doc.designMode="on";
23984         this.doc.open();
23985         this.doc.write(this.getDocMarkup());
23986         this.doc.close();
23987         
23988         var dbody = (this.doc.body || this.doc.documentElement);
23989         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
23990         // this copies styles from the containing element into thsi one..
23991         // not sure why we need all of this..
23992         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
23993         
23994         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
23995         //ss['background-attachment'] = 'fixed'; // w3c
23996         dbody.bgProperties = 'fixed'; // ie
23997         //Roo.DomHelper.applyStyles(dbody, ss);
23998         Roo.EventManager.on(this.doc, {
23999             //'mousedown': this.onEditorEvent,
24000             'mouseup': this.onEditorEvent,
24001             'dblclick': this.onEditorEvent,
24002             'click': this.onEditorEvent,
24003             'keyup': this.onEditorEvent,
24004             buffer:100,
24005             scope: this
24006         });
24007         if(Roo.isGecko){
24008             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24009         }
24010         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24011             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24012         }
24013         this.initialized = true;
24014
24015         this.owner.fireEvent('initialize', this);
24016         this.pushValue();
24017     },
24018
24019     // private
24020     onDestroy : function(){
24021         
24022         
24023         
24024         if(this.rendered){
24025             
24026             //for (var i =0; i < this.toolbars.length;i++) {
24027             //    // fixme - ask toolbars for heights?
24028             //    this.toolbars[i].onDestroy();
24029            // }
24030             
24031             //this.wrap.dom.innerHTML = '';
24032             //this.wrap.remove();
24033         }
24034     },
24035
24036     // private
24037     onFirstFocus : function(){
24038         
24039         this.assignDocWin();
24040         
24041         
24042         this.activated = true;
24043          
24044     
24045         if(Roo.isGecko){ // prevent silly gecko errors
24046             this.win.focus();
24047             var s = this.win.getSelection();
24048             if(!s.focusNode || s.focusNode.nodeType != 3){
24049                 var r = s.getRangeAt(0);
24050                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24051                 r.collapse(true);
24052                 this.deferFocus();
24053             }
24054             try{
24055                 this.execCmd('useCSS', true);
24056                 this.execCmd('styleWithCSS', false);
24057             }catch(e){}
24058         }
24059         this.owner.fireEvent('activate', this);
24060     },
24061
24062     // private
24063     adjustFont: function(btn){
24064         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24065         //if(Roo.isSafari){ // safari
24066         //    adjust *= 2;
24067        // }
24068         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24069         if(Roo.isSafari){ // safari
24070             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24071             v =  (v < 10) ? 10 : v;
24072             v =  (v > 48) ? 48 : v;
24073             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24074             
24075         }
24076         
24077         
24078         v = Math.max(1, v+adjust);
24079         
24080         this.execCmd('FontSize', v  );
24081     },
24082
24083     onEditorEvent : function(e)
24084     {
24085         this.owner.fireEvent('editorevent', this, e);
24086       //  this.updateToolbar();
24087         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24088     },
24089
24090     insertTag : function(tg)
24091     {
24092         // could be a bit smarter... -> wrap the current selected tRoo..
24093         if (tg.toLowerCase() == 'span' ||
24094             tg.toLowerCase() == 'code' ||
24095             tg.toLowerCase() == 'sup' ||
24096             tg.toLowerCase() == 'sub' 
24097             ) {
24098             
24099             range = this.createRange(this.getSelection());
24100             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24101             wrappingNode.appendChild(range.extractContents());
24102             range.insertNode(wrappingNode);
24103
24104             return;
24105             
24106             
24107             
24108         }
24109         this.execCmd("formatblock",   tg);
24110         
24111     },
24112     
24113     insertText : function(txt)
24114     {
24115         
24116         
24117         var range = this.createRange();
24118         range.deleteContents();
24119                //alert(Sender.getAttribute('label'));
24120                
24121         range.insertNode(this.doc.createTextNode(txt));
24122     } ,
24123     
24124      
24125
24126     /**
24127      * Executes a Midas editor command on the editor document and performs necessary focus and
24128      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24129      * @param {String} cmd The Midas command
24130      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24131      */
24132     relayCmd : function(cmd, value){
24133         this.win.focus();
24134         this.execCmd(cmd, value);
24135         this.owner.fireEvent('editorevent', this);
24136         //this.updateToolbar();
24137         this.owner.deferFocus();
24138     },
24139
24140     /**
24141      * Executes a Midas editor command directly on the editor document.
24142      * For visual commands, you should use {@link #relayCmd} instead.
24143      * <b>This should only be called after the editor is initialized.</b>
24144      * @param {String} cmd The Midas command
24145      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24146      */
24147     execCmd : function(cmd, value){
24148         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24149         this.syncValue();
24150     },
24151  
24152  
24153    
24154     /**
24155      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24156      * to insert tRoo.
24157      * @param {String} text | dom node.. 
24158      */
24159     insertAtCursor : function(text)
24160     {
24161         
24162         if(!this.activated){
24163             return;
24164         }
24165         /*
24166         if(Roo.isIE){
24167             this.win.focus();
24168             var r = this.doc.selection.createRange();
24169             if(r){
24170                 r.collapse(true);
24171                 r.pasteHTML(text);
24172                 this.syncValue();
24173                 this.deferFocus();
24174             
24175             }
24176             return;
24177         }
24178         */
24179         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24180             this.win.focus();
24181             
24182             
24183             // from jquery ui (MIT licenced)
24184             var range, node;
24185             var win = this.win;
24186             
24187             if (win.getSelection && win.getSelection().getRangeAt) {
24188                 range = win.getSelection().getRangeAt(0);
24189                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24190                 range.insertNode(node);
24191             } else if (win.document.selection && win.document.selection.createRange) {
24192                 // no firefox support
24193                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24194                 win.document.selection.createRange().pasteHTML(txt);
24195             } else {
24196                 // no firefox support
24197                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24198                 this.execCmd('InsertHTML', txt);
24199             } 
24200             
24201             this.syncValue();
24202             
24203             this.deferFocus();
24204         }
24205     },
24206  // private
24207     mozKeyPress : function(e){
24208         if(e.ctrlKey){
24209             var c = e.getCharCode(), cmd;
24210           
24211             if(c > 0){
24212                 c = String.fromCharCode(c).toLowerCase();
24213                 switch(c){
24214                     case 'b':
24215                         cmd = 'bold';
24216                         break;
24217                     case 'i':
24218                         cmd = 'italic';
24219                         break;
24220                     
24221                     case 'u':
24222                         cmd = 'underline';
24223                         break;
24224                     
24225                     case 'v':
24226                         this.cleanUpPaste.defer(100, this);
24227                         return;
24228                         
24229                 }
24230                 if(cmd){
24231                     this.win.focus();
24232                     this.execCmd(cmd);
24233                     this.deferFocus();
24234                     e.preventDefault();
24235                 }
24236                 
24237             }
24238         }
24239     },
24240
24241     // private
24242     fixKeys : function(){ // load time branching for fastest keydown performance
24243         if(Roo.isIE){
24244             return function(e){
24245                 var k = e.getKey(), r;
24246                 if(k == e.TAB){
24247                     e.stopEvent();
24248                     r = this.doc.selection.createRange();
24249                     if(r){
24250                         r.collapse(true);
24251                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24252                         this.deferFocus();
24253                     }
24254                     return;
24255                 }
24256                 
24257                 if(k == e.ENTER){
24258                     r = this.doc.selection.createRange();
24259                     if(r){
24260                         var target = r.parentElement();
24261                         if(!target || target.tagName.toLowerCase() != 'li'){
24262                             e.stopEvent();
24263                             r.pasteHTML('<br />');
24264                             r.collapse(false);
24265                             r.select();
24266                         }
24267                     }
24268                 }
24269                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24270                     this.cleanUpPaste.defer(100, this);
24271                     return;
24272                 }
24273                 
24274                 
24275             };
24276         }else if(Roo.isOpera){
24277             return function(e){
24278                 var k = e.getKey();
24279                 if(k == e.TAB){
24280                     e.stopEvent();
24281                     this.win.focus();
24282                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24283                     this.deferFocus();
24284                 }
24285                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24286                     this.cleanUpPaste.defer(100, this);
24287                     return;
24288                 }
24289                 
24290             };
24291         }else if(Roo.isSafari){
24292             return function(e){
24293                 var k = e.getKey();
24294                 
24295                 if(k == e.TAB){
24296                     e.stopEvent();
24297                     this.execCmd('InsertText','\t');
24298                     this.deferFocus();
24299                     return;
24300                 }
24301                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24302                     this.cleanUpPaste.defer(100, this);
24303                     return;
24304                 }
24305                 
24306              };
24307         }
24308     }(),
24309     
24310     getAllAncestors: function()
24311     {
24312         var p = this.getSelectedNode();
24313         var a = [];
24314         if (!p) {
24315             a.push(p); // push blank onto stack..
24316             p = this.getParentElement();
24317         }
24318         
24319         
24320         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24321             a.push(p);
24322             p = p.parentNode;
24323         }
24324         a.push(this.doc.body);
24325         return a;
24326     },
24327     lastSel : false,
24328     lastSelNode : false,
24329     
24330     
24331     getSelection : function() 
24332     {
24333         this.assignDocWin();
24334         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24335     },
24336     
24337     getSelectedNode: function() 
24338     {
24339         // this may only work on Gecko!!!
24340         
24341         // should we cache this!!!!
24342         
24343         
24344         
24345          
24346         var range = this.createRange(this.getSelection()).cloneRange();
24347         
24348         if (Roo.isIE) {
24349             var parent = range.parentElement();
24350             while (true) {
24351                 var testRange = range.duplicate();
24352                 testRange.moveToElementText(parent);
24353                 if (testRange.inRange(range)) {
24354                     break;
24355                 }
24356                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24357                     break;
24358                 }
24359                 parent = parent.parentElement;
24360             }
24361             return parent;
24362         }
24363         
24364         // is ancestor a text element.
24365         var ac =  range.commonAncestorContainer;
24366         if (ac.nodeType == 3) {
24367             ac = ac.parentNode;
24368         }
24369         
24370         var ar = ac.childNodes;
24371          
24372         var nodes = [];
24373         var other_nodes = [];
24374         var has_other_nodes = false;
24375         for (var i=0;i<ar.length;i++) {
24376             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24377                 continue;
24378             }
24379             // fullly contained node.
24380             
24381             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24382                 nodes.push(ar[i]);
24383                 continue;
24384             }
24385             
24386             // probably selected..
24387             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24388                 other_nodes.push(ar[i]);
24389                 continue;
24390             }
24391             // outer..
24392             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24393                 continue;
24394             }
24395             
24396             
24397             has_other_nodes = true;
24398         }
24399         if (!nodes.length && other_nodes.length) {
24400             nodes= other_nodes;
24401         }
24402         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24403             return false;
24404         }
24405         
24406         return nodes[0];
24407     },
24408     createRange: function(sel)
24409     {
24410         // this has strange effects when using with 
24411         // top toolbar - not sure if it's a great idea.
24412         //this.editor.contentWindow.focus();
24413         if (typeof sel != "undefined") {
24414             try {
24415                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24416             } catch(e) {
24417                 return this.doc.createRange();
24418             }
24419         } else {
24420             return this.doc.createRange();
24421         }
24422     },
24423     getParentElement: function()
24424     {
24425         
24426         this.assignDocWin();
24427         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24428         
24429         var range = this.createRange(sel);
24430          
24431         try {
24432             var p = range.commonAncestorContainer;
24433             while (p.nodeType == 3) { // text node
24434                 p = p.parentNode;
24435             }
24436             return p;
24437         } catch (e) {
24438             return null;
24439         }
24440     
24441     },
24442     /***
24443      *
24444      * Range intersection.. the hard stuff...
24445      *  '-1' = before
24446      *  '0' = hits..
24447      *  '1' = after.
24448      *         [ -- selected range --- ]
24449      *   [fail]                        [fail]
24450      *
24451      *    basically..
24452      *      if end is before start or  hits it. fail.
24453      *      if start is after end or hits it fail.
24454      *
24455      *   if either hits (but other is outside. - then it's not 
24456      *   
24457      *    
24458      **/
24459     
24460     
24461     // @see http://www.thismuchiknow.co.uk/?p=64.
24462     rangeIntersectsNode : function(range, node)
24463     {
24464         var nodeRange = node.ownerDocument.createRange();
24465         try {
24466             nodeRange.selectNode(node);
24467         } catch (e) {
24468             nodeRange.selectNodeContents(node);
24469         }
24470     
24471         var rangeStartRange = range.cloneRange();
24472         rangeStartRange.collapse(true);
24473     
24474         var rangeEndRange = range.cloneRange();
24475         rangeEndRange.collapse(false);
24476     
24477         var nodeStartRange = nodeRange.cloneRange();
24478         nodeStartRange.collapse(true);
24479     
24480         var nodeEndRange = nodeRange.cloneRange();
24481         nodeEndRange.collapse(false);
24482     
24483         return rangeStartRange.compareBoundaryPoints(
24484                  Range.START_TO_START, nodeEndRange) == -1 &&
24485                rangeEndRange.compareBoundaryPoints(
24486                  Range.START_TO_START, nodeStartRange) == 1;
24487         
24488          
24489     },
24490     rangeCompareNode : function(range, node)
24491     {
24492         var nodeRange = node.ownerDocument.createRange();
24493         try {
24494             nodeRange.selectNode(node);
24495         } catch (e) {
24496             nodeRange.selectNodeContents(node);
24497         }
24498         
24499         
24500         range.collapse(true);
24501     
24502         nodeRange.collapse(true);
24503      
24504         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24505         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24506          
24507         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24508         
24509         var nodeIsBefore   =  ss == 1;
24510         var nodeIsAfter    = ee == -1;
24511         
24512         if (nodeIsBefore && nodeIsAfter) {
24513             return 0; // outer
24514         }
24515         if (!nodeIsBefore && nodeIsAfter) {
24516             return 1; //right trailed.
24517         }
24518         
24519         if (nodeIsBefore && !nodeIsAfter) {
24520             return 2;  // left trailed.
24521         }
24522         // fully contined.
24523         return 3;
24524     },
24525
24526     // private? - in a new class?
24527     cleanUpPaste :  function()
24528     {
24529         // cleans up the whole document..
24530         Roo.log('cleanuppaste');
24531         
24532         this.cleanUpChildren(this.doc.body);
24533         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24534         if (clean != this.doc.body.innerHTML) {
24535             this.doc.body.innerHTML = clean;
24536         }
24537         
24538     },
24539     
24540     cleanWordChars : function(input) {// change the chars to hex code
24541         var he = Roo.HtmlEditorCore;
24542         
24543         var output = input;
24544         Roo.each(he.swapCodes, function(sw) { 
24545             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24546             
24547             output = output.replace(swapper, sw[1]);
24548         });
24549         
24550         return output;
24551     },
24552     
24553     
24554     cleanUpChildren : function (n)
24555     {
24556         if (!n.childNodes.length) {
24557             return;
24558         }
24559         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24560            this.cleanUpChild(n.childNodes[i]);
24561         }
24562     },
24563     
24564     
24565         
24566     
24567     cleanUpChild : function (node)
24568     {
24569         var ed = this;
24570         //console.log(node);
24571         if (node.nodeName == "#text") {
24572             // clean up silly Windows -- stuff?
24573             return; 
24574         }
24575         if (node.nodeName == "#comment") {
24576             node.parentNode.removeChild(node);
24577             // clean up silly Windows -- stuff?
24578             return; 
24579         }
24580         var lcname = node.tagName.toLowerCase();
24581         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24582         // whitelist of tags..
24583         
24584         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24585             // remove node.
24586             node.parentNode.removeChild(node);
24587             return;
24588             
24589         }
24590         
24591         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24592         
24593         // spans with no attributes - just remove them..
24594         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24595             remove_keep_children = true;
24596         }
24597         
24598         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24599         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24600         
24601         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24602         //    remove_keep_children = true;
24603         //}
24604         
24605         if (remove_keep_children) {
24606             this.cleanUpChildren(node);
24607             // inserts everything just before this node...
24608             while (node.childNodes.length) {
24609                 var cn = node.childNodes[0];
24610                 node.removeChild(cn);
24611                 node.parentNode.insertBefore(cn, node);
24612             }
24613             node.parentNode.removeChild(node);
24614             return;
24615         }
24616         
24617         if (!node.attributes || !node.attributes.length) {
24618             
24619           
24620             
24621             
24622             this.cleanUpChildren(node);
24623             return;
24624         }
24625         
24626         function cleanAttr(n,v)
24627         {
24628             
24629             if (v.match(/^\./) || v.match(/^\//)) {
24630                 return;
24631             }
24632             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24633                 return;
24634             }
24635             if (v.match(/^#/)) {
24636                 return;
24637             }
24638 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24639             node.removeAttribute(n);
24640             
24641         }
24642         
24643         var cwhite = this.cwhite;
24644         var cblack = this.cblack;
24645             
24646         function cleanStyle(n,v)
24647         {
24648             if (v.match(/expression/)) { //XSS?? should we even bother..
24649                 node.removeAttribute(n);
24650                 return;
24651             }
24652             
24653             var parts = v.split(/;/);
24654             var clean = [];
24655             
24656             Roo.each(parts, function(p) {
24657                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24658                 if (!p.length) {
24659                     return true;
24660                 }
24661                 var l = p.split(':').shift().replace(/\s+/g,'');
24662                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24663                 
24664                 if ( cwhite.length && cblack.indexOf(l) > -1) {
24665 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24666                     //node.removeAttribute(n);
24667                     return true;
24668                 }
24669                 //Roo.log()
24670                 // only allow 'c whitelisted system attributes'
24671                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
24672 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24673                     //node.removeAttribute(n);
24674                     return true;
24675                 }
24676                 
24677                 
24678                  
24679                 
24680                 clean.push(p);
24681                 return true;
24682             });
24683             if (clean.length) { 
24684                 node.setAttribute(n, clean.join(';'));
24685             } else {
24686                 node.removeAttribute(n);
24687             }
24688             
24689         }
24690         
24691         
24692         for (var i = node.attributes.length-1; i > -1 ; i--) {
24693             var a = node.attributes[i];
24694             //console.log(a);
24695             
24696             if (a.name.toLowerCase().substr(0,2)=='on')  {
24697                 node.removeAttribute(a.name);
24698                 continue;
24699             }
24700             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24701                 node.removeAttribute(a.name);
24702                 continue;
24703             }
24704             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24705                 cleanAttr(a.name,a.value); // fixme..
24706                 continue;
24707             }
24708             if (a.name == 'style') {
24709                 cleanStyle(a.name,a.value);
24710                 continue;
24711             }
24712             /// clean up MS crap..
24713             // tecnically this should be a list of valid class'es..
24714             
24715             
24716             if (a.name == 'class') {
24717                 if (a.value.match(/^Mso/)) {
24718                     node.removeAttribute('class');
24719                 }
24720                 
24721                 if (a.value.match(/^body$/)) {
24722                     node.removeAttribute('class');
24723                 }
24724                 continue;
24725             }
24726             
24727             // style cleanup!?
24728             // class cleanup?
24729             
24730         }
24731         
24732         
24733         this.cleanUpChildren(node);
24734         
24735         
24736     },
24737     
24738     /**
24739      * Clean up MS wordisms...
24740      */
24741     cleanWord : function(node)
24742     {
24743         if (!node) {
24744             this.cleanWord(this.doc.body);
24745             return;
24746         }
24747         
24748         if(
24749                 node.nodeName == 'SPAN' &&
24750                 !node.hasAttributes() &&
24751                 node.childNodes.length == 1 &&
24752                 node.firstChild.nodeName == "#text"  
24753         ) {
24754             var textNode = node.firstChild;
24755             node.removeChild(textNode);
24756             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24757                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
24758             }
24759             node.parentNode.insertBefore(textNode, node);
24760             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24761                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24762             }
24763             node.parentNode.removeChild(node);
24764         }
24765         
24766         if (node.nodeName == "#text") {
24767             // clean up silly Windows -- stuff?
24768             return; 
24769         }
24770         if (node.nodeName == "#comment") {
24771             node.parentNode.removeChild(node);
24772             // clean up silly Windows -- stuff?
24773             return; 
24774         }
24775         
24776         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24777             node.parentNode.removeChild(node);
24778             return;
24779         }
24780         //Roo.log(node.tagName);
24781         // remove - but keep children..
24782         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24783             //Roo.log('-- removed');
24784             while (node.childNodes.length) {
24785                 var cn = node.childNodes[0];
24786                 node.removeChild(cn);
24787                 node.parentNode.insertBefore(cn, node);
24788                 // move node to parent - and clean it..
24789                 this.cleanWord(cn);
24790             }
24791             node.parentNode.removeChild(node);
24792             /// no need to iterate chidlren = it's got none..
24793             //this.iterateChildren(node, this.cleanWord);
24794             return;
24795         }
24796         // clean styles
24797         if (node.className.length) {
24798             
24799             var cn = node.className.split(/\W+/);
24800             var cna = [];
24801             Roo.each(cn, function(cls) {
24802                 if (cls.match(/Mso[a-zA-Z]+/)) {
24803                     return;
24804                 }
24805                 cna.push(cls);
24806             });
24807             node.className = cna.length ? cna.join(' ') : '';
24808             if (!cna.length) {
24809                 node.removeAttribute("class");
24810             }
24811         }
24812         
24813         if (node.hasAttribute("lang")) {
24814             node.removeAttribute("lang");
24815         }
24816         
24817         if (node.hasAttribute("style")) {
24818             
24819             var styles = node.getAttribute("style").split(";");
24820             var nstyle = [];
24821             Roo.each(styles, function(s) {
24822                 if (!s.match(/:/)) {
24823                     return;
24824                 }
24825                 var kv = s.split(":");
24826                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24827                     return;
24828                 }
24829                 // what ever is left... we allow.
24830                 nstyle.push(s);
24831             });
24832             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24833             if (!nstyle.length) {
24834                 node.removeAttribute('style');
24835             }
24836         }
24837         this.iterateChildren(node, this.cleanWord);
24838         
24839         
24840         
24841     },
24842     /**
24843      * iterateChildren of a Node, calling fn each time, using this as the scole..
24844      * @param {DomNode} node node to iterate children of.
24845      * @param {Function} fn method of this class to call on each item.
24846      */
24847     iterateChildren : function(node, fn)
24848     {
24849         if (!node.childNodes.length) {
24850                 return;
24851         }
24852         for (var i = node.childNodes.length-1; i > -1 ; i--) {
24853            fn.call(this, node.childNodes[i])
24854         }
24855     },
24856     
24857     
24858     /**
24859      * cleanTableWidths.
24860      *
24861      * Quite often pasting from word etc.. results in tables with column and widths.
24862      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24863      *
24864      */
24865     cleanTableWidths : function(node)
24866     {
24867          
24868          
24869         if (!node) {
24870             this.cleanTableWidths(this.doc.body);
24871             return;
24872         }
24873         
24874         // ignore list...
24875         if (node.nodeName == "#text" || node.nodeName == "#comment") {
24876             return; 
24877         }
24878         Roo.log(node.tagName);
24879         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24880             this.iterateChildren(node, this.cleanTableWidths);
24881             return;
24882         }
24883         if (node.hasAttribute('width')) {
24884             node.removeAttribute('width');
24885         }
24886         
24887          
24888         if (node.hasAttribute("style")) {
24889             // pretty basic...
24890             
24891             var styles = node.getAttribute("style").split(";");
24892             var nstyle = [];
24893             Roo.each(styles, function(s) {
24894                 if (!s.match(/:/)) {
24895                     return;
24896                 }
24897                 var kv = s.split(":");
24898                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
24899                     return;
24900                 }
24901                 // what ever is left... we allow.
24902                 nstyle.push(s);
24903             });
24904             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24905             if (!nstyle.length) {
24906                 node.removeAttribute('style');
24907             }
24908         }
24909         
24910         this.iterateChildren(node, this.cleanTableWidths);
24911         
24912         
24913     },
24914     
24915     
24916     
24917     
24918     domToHTML : function(currentElement, depth, nopadtext) {
24919         
24920         depth = depth || 0;
24921         nopadtext = nopadtext || false;
24922     
24923         if (!currentElement) {
24924             return this.domToHTML(this.doc.body);
24925         }
24926         
24927         //Roo.log(currentElement);
24928         var j;
24929         var allText = false;
24930         var nodeName = currentElement.nodeName;
24931         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
24932         
24933         if  (nodeName == '#text') {
24934             
24935             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
24936         }
24937         
24938         
24939         var ret = '';
24940         if (nodeName != 'BODY') {
24941              
24942             var i = 0;
24943             // Prints the node tagName, such as <A>, <IMG>, etc
24944             if (tagName) {
24945                 var attr = [];
24946                 for(i = 0; i < currentElement.attributes.length;i++) {
24947                     // quoting?
24948                     var aname = currentElement.attributes.item(i).name;
24949                     if (!currentElement.attributes.item(i).value.length) {
24950                         continue;
24951                     }
24952                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
24953                 }
24954                 
24955                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
24956             } 
24957             else {
24958                 
24959                 // eack
24960             }
24961         } else {
24962             tagName = false;
24963         }
24964         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
24965             return ret;
24966         }
24967         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
24968             nopadtext = true;
24969         }
24970         
24971         
24972         // Traverse the tree
24973         i = 0;
24974         var currentElementChild = currentElement.childNodes.item(i);
24975         var allText = true;
24976         var innerHTML  = '';
24977         lastnode = '';
24978         while (currentElementChild) {
24979             // Formatting code (indent the tree so it looks nice on the screen)
24980             var nopad = nopadtext;
24981             if (lastnode == 'SPAN') {
24982                 nopad  = true;
24983             }
24984             // text
24985             if  (currentElementChild.nodeName == '#text') {
24986                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
24987                 toadd = nopadtext ? toadd : toadd.trim();
24988                 if (!nopad && toadd.length > 80) {
24989                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
24990                 }
24991                 innerHTML  += toadd;
24992                 
24993                 i++;
24994                 currentElementChild = currentElement.childNodes.item(i);
24995                 lastNode = '';
24996                 continue;
24997             }
24998             allText = false;
24999             
25000             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25001                 
25002             // Recursively traverse the tree structure of the child node
25003             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25004             lastnode = currentElementChild.nodeName;
25005             i++;
25006             currentElementChild=currentElement.childNodes.item(i);
25007         }
25008         
25009         ret += innerHTML;
25010         
25011         if (!allText) {
25012                 // The remaining code is mostly for formatting the tree
25013             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25014         }
25015         
25016         
25017         if (tagName) {
25018             ret+= "</"+tagName+">";
25019         }
25020         return ret;
25021         
25022     },
25023         
25024     applyBlacklists : function()
25025     {
25026         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25027         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25028         
25029         this.white = [];
25030         this.black = [];
25031         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25032             if (b.indexOf(tag) > -1) {
25033                 return;
25034             }
25035             this.white.push(tag);
25036             
25037         }, this);
25038         
25039         Roo.each(w, function(tag) {
25040             if (b.indexOf(tag) > -1) {
25041                 return;
25042             }
25043             if (this.white.indexOf(tag) > -1) {
25044                 return;
25045             }
25046             this.white.push(tag);
25047             
25048         }, this);
25049         
25050         
25051         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25052             if (w.indexOf(tag) > -1) {
25053                 return;
25054             }
25055             this.black.push(tag);
25056             
25057         }, this);
25058         
25059         Roo.each(b, function(tag) {
25060             if (w.indexOf(tag) > -1) {
25061                 return;
25062             }
25063             if (this.black.indexOf(tag) > -1) {
25064                 return;
25065             }
25066             this.black.push(tag);
25067             
25068         }, this);
25069         
25070         
25071         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25072         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25073         
25074         this.cwhite = [];
25075         this.cblack = [];
25076         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25077             if (b.indexOf(tag) > -1) {
25078                 return;
25079             }
25080             this.cwhite.push(tag);
25081             
25082         }, this);
25083         
25084         Roo.each(w, function(tag) {
25085             if (b.indexOf(tag) > -1) {
25086                 return;
25087             }
25088             if (this.cwhite.indexOf(tag) > -1) {
25089                 return;
25090             }
25091             this.cwhite.push(tag);
25092             
25093         }, this);
25094         
25095         
25096         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25097             if (w.indexOf(tag) > -1) {
25098                 return;
25099             }
25100             this.cblack.push(tag);
25101             
25102         }, this);
25103         
25104         Roo.each(b, function(tag) {
25105             if (w.indexOf(tag) > -1) {
25106                 return;
25107             }
25108             if (this.cblack.indexOf(tag) > -1) {
25109                 return;
25110             }
25111             this.cblack.push(tag);
25112             
25113         }, this);
25114     },
25115     
25116     setStylesheets : function(stylesheets)
25117     {
25118         if(typeof(stylesheets) == 'string'){
25119             Roo.get(this.iframe.contentDocument.head).createChild({
25120                 tag : 'link',
25121                 rel : 'stylesheet',
25122                 type : 'text/css',
25123                 href : stylesheets
25124             });
25125             
25126             return;
25127         }
25128         var _this = this;
25129      
25130         Roo.each(stylesheets, function(s) {
25131             if(!s.length){
25132                 return;
25133             }
25134             
25135             Roo.get(_this.iframe.contentDocument.head).createChild({
25136                 tag : 'link',
25137                 rel : 'stylesheet',
25138                 type : 'text/css',
25139                 href : s
25140             });
25141         });
25142
25143         
25144     },
25145     
25146     removeStylesheets : function()
25147     {
25148         var _this = this;
25149         
25150         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25151             s.remove();
25152         });
25153     },
25154     
25155     setStyle : function(style)
25156     {
25157         Roo.get(this.iframe.contentDocument.head).createChild({
25158             tag : 'style',
25159             type : 'text/css',
25160             html : style
25161         });
25162
25163         return;
25164     }
25165     
25166     // hide stuff that is not compatible
25167     /**
25168      * @event blur
25169      * @hide
25170      */
25171     /**
25172      * @event change
25173      * @hide
25174      */
25175     /**
25176      * @event focus
25177      * @hide
25178      */
25179     /**
25180      * @event specialkey
25181      * @hide
25182      */
25183     /**
25184      * @cfg {String} fieldClass @hide
25185      */
25186     /**
25187      * @cfg {String} focusClass @hide
25188      */
25189     /**
25190      * @cfg {String} autoCreate @hide
25191      */
25192     /**
25193      * @cfg {String} inputType @hide
25194      */
25195     /**
25196      * @cfg {String} invalidClass @hide
25197      */
25198     /**
25199      * @cfg {String} invalidText @hide
25200      */
25201     /**
25202      * @cfg {String} msgFx @hide
25203      */
25204     /**
25205      * @cfg {String} validateOnBlur @hide
25206      */
25207 });
25208
25209 Roo.HtmlEditorCore.white = [
25210         'area', 'br', 'img', 'input', 'hr', 'wbr',
25211         
25212        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25213        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25214        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25215        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25216        'table',   'ul',         'xmp', 
25217        
25218        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25219       'thead',   'tr', 
25220      
25221       'dir', 'menu', 'ol', 'ul', 'dl',
25222        
25223       'embed',  'object'
25224 ];
25225
25226
25227 Roo.HtmlEditorCore.black = [
25228     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25229         'applet', // 
25230         'base',   'basefont', 'bgsound', 'blink',  'body', 
25231         'frame',  'frameset', 'head',    'html',   'ilayer', 
25232         'iframe', 'layer',  'link',     'meta',    'object',   
25233         'script', 'style' ,'title',  'xml' // clean later..
25234 ];
25235 Roo.HtmlEditorCore.clean = [
25236     'script', 'style', 'title', 'xml'
25237 ];
25238 Roo.HtmlEditorCore.remove = [
25239     'font'
25240 ];
25241 // attributes..
25242
25243 Roo.HtmlEditorCore.ablack = [
25244     'on'
25245 ];
25246     
25247 Roo.HtmlEditorCore.aclean = [ 
25248     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25249 ];
25250
25251 // protocols..
25252 Roo.HtmlEditorCore.pwhite= [
25253         'http',  'https',  'mailto'
25254 ];
25255
25256 // white listed style attributes.
25257 Roo.HtmlEditorCore.cwhite= [
25258       //  'text-align', /// default is to allow most things..
25259       
25260          
25261 //        'font-size'//??
25262 ];
25263
25264 // black listed style attributes.
25265 Roo.HtmlEditorCore.cblack= [
25266       //  'font-size' -- this can be set by the project 
25267 ];
25268
25269
25270 Roo.HtmlEditorCore.swapCodes   =[ 
25271     [    8211, "--" ], 
25272     [    8212, "--" ], 
25273     [    8216,  "'" ],  
25274     [    8217, "'" ],  
25275     [    8220, '"' ],  
25276     [    8221, '"' ],  
25277     [    8226, "*" ],  
25278     [    8230, "..." ]
25279 ]; 
25280
25281     /*
25282  * - LGPL
25283  *
25284  * HtmlEditor
25285  * 
25286  */
25287
25288 /**
25289  * @class Roo.bootstrap.HtmlEditor
25290  * @extends Roo.bootstrap.TextArea
25291  * Bootstrap HtmlEditor class
25292
25293  * @constructor
25294  * Create a new HtmlEditor
25295  * @param {Object} config The config object
25296  */
25297
25298 Roo.bootstrap.HtmlEditor = function(config){
25299     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25300     if (!this.toolbars) {
25301         this.toolbars = [];
25302     }
25303     
25304     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25305     this.addEvents({
25306             /**
25307              * @event initialize
25308              * Fires when the editor is fully initialized (including the iframe)
25309              * @param {HtmlEditor} this
25310              */
25311             initialize: true,
25312             /**
25313              * @event activate
25314              * Fires when the editor is first receives the focus. Any insertion must wait
25315              * until after this event.
25316              * @param {HtmlEditor} this
25317              */
25318             activate: true,
25319              /**
25320              * @event beforesync
25321              * Fires before the textarea is updated with content from the editor iframe. Return false
25322              * to cancel the sync.
25323              * @param {HtmlEditor} this
25324              * @param {String} html
25325              */
25326             beforesync: true,
25327              /**
25328              * @event beforepush
25329              * Fires before the iframe editor is updated with content from the textarea. Return false
25330              * to cancel the push.
25331              * @param {HtmlEditor} this
25332              * @param {String} html
25333              */
25334             beforepush: true,
25335              /**
25336              * @event sync
25337              * Fires when the textarea is updated with content from the editor iframe.
25338              * @param {HtmlEditor} this
25339              * @param {String} html
25340              */
25341             sync: true,
25342              /**
25343              * @event push
25344              * Fires when the iframe editor is updated with content from the textarea.
25345              * @param {HtmlEditor} this
25346              * @param {String} html
25347              */
25348             push: true,
25349              /**
25350              * @event editmodechange
25351              * Fires when the editor switches edit modes
25352              * @param {HtmlEditor} this
25353              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25354              */
25355             editmodechange: true,
25356             /**
25357              * @event editorevent
25358              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25359              * @param {HtmlEditor} this
25360              */
25361             editorevent: true,
25362             /**
25363              * @event firstfocus
25364              * Fires when on first focus - needed by toolbars..
25365              * @param {HtmlEditor} this
25366              */
25367             firstfocus: true,
25368             /**
25369              * @event autosave
25370              * Auto save the htmlEditor value as a file into Events
25371              * @param {HtmlEditor} this
25372              */
25373             autosave: true,
25374             /**
25375              * @event savedpreview
25376              * preview the saved version of htmlEditor
25377              * @param {HtmlEditor} this
25378              */
25379             savedpreview: true
25380         });
25381 };
25382
25383
25384 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25385     
25386     
25387       /**
25388      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25389      */
25390     toolbars : false,
25391     
25392      /**
25393     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25394     */
25395     btns : [],
25396    
25397      /**
25398      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25399      *                        Roo.resizable.
25400      */
25401     resizable : false,
25402      /**
25403      * @cfg {Number} height (in pixels)
25404      */   
25405     height: 300,
25406    /**
25407      * @cfg {Number} width (in pixels)
25408      */   
25409     width: false,
25410     
25411     /**
25412      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25413      * 
25414      */
25415     stylesheets: false,
25416     
25417     // id of frame..
25418     frameId: false,
25419     
25420     // private properties
25421     validationEvent : false,
25422     deferHeight: true,
25423     initialized : false,
25424     activated : false,
25425     
25426     onFocus : Roo.emptyFn,
25427     iframePad:3,
25428     hideMode:'offsets',
25429     
25430     tbContainer : false,
25431     
25432     bodyCls : '',
25433     
25434     toolbarContainer :function() {
25435         return this.wrap.select('.x-html-editor-tb',true).first();
25436     },
25437
25438     /**
25439      * Protected method that will not generally be called directly. It
25440      * is called when the editor creates its toolbar. Override this method if you need to
25441      * add custom toolbar buttons.
25442      * @param {HtmlEditor} editor
25443      */
25444     createToolbar : function(){
25445         Roo.log('renewing');
25446         Roo.log("create toolbars");
25447         
25448         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25449         this.toolbars[0].render(this.toolbarContainer());
25450         
25451         return;
25452         
25453 //        if (!editor.toolbars || !editor.toolbars.length) {
25454 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25455 //        }
25456 //        
25457 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25458 //            editor.toolbars[i] = Roo.factory(
25459 //                    typeof(editor.toolbars[i]) == 'string' ?
25460 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25461 //                Roo.bootstrap.HtmlEditor);
25462 //            editor.toolbars[i].init(editor);
25463 //        }
25464     },
25465
25466      
25467     // private
25468     onRender : function(ct, position)
25469     {
25470        // Roo.log("Call onRender: " + this.xtype);
25471         var _t = this;
25472         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25473       
25474         this.wrap = this.inputEl().wrap({
25475             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25476         });
25477         
25478         this.editorcore.onRender(ct, position);
25479          
25480         if (this.resizable) {
25481             this.resizeEl = new Roo.Resizable(this.wrap, {
25482                 pinned : true,
25483                 wrap: true,
25484                 dynamic : true,
25485                 minHeight : this.height,
25486                 height: this.height,
25487                 handles : this.resizable,
25488                 width: this.width,
25489                 listeners : {
25490                     resize : function(r, w, h) {
25491                         _t.onResize(w,h); // -something
25492                     }
25493                 }
25494             });
25495             
25496         }
25497         this.createToolbar(this);
25498        
25499         
25500         if(!this.width && this.resizable){
25501             this.setSize(this.wrap.getSize());
25502         }
25503         if (this.resizeEl) {
25504             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25505             // should trigger onReize..
25506         }
25507         
25508     },
25509
25510     // private
25511     onResize : function(w, h)
25512     {
25513         Roo.log('resize: ' +w + ',' + h );
25514         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25515         var ew = false;
25516         var eh = false;
25517         
25518         if(this.inputEl() ){
25519             if(typeof w == 'number'){
25520                 var aw = w - this.wrap.getFrameWidth('lr');
25521                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25522                 ew = aw;
25523             }
25524             if(typeof h == 'number'){
25525                  var tbh = -11;  // fixme it needs to tool bar size!
25526                 for (var i =0; i < this.toolbars.length;i++) {
25527                     // fixme - ask toolbars for heights?
25528                     tbh += this.toolbars[i].el.getHeight();
25529                     //if (this.toolbars[i].footer) {
25530                     //    tbh += this.toolbars[i].footer.el.getHeight();
25531                     //}
25532                 }
25533               
25534                 
25535                 
25536                 
25537                 
25538                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25539                 ah -= 5; // knock a few pixes off for look..
25540                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25541                 var eh = ah;
25542             }
25543         }
25544         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25545         this.editorcore.onResize(ew,eh);
25546         
25547     },
25548
25549     /**
25550      * Toggles the editor between standard and source edit mode.
25551      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25552      */
25553     toggleSourceEdit : function(sourceEditMode)
25554     {
25555         this.editorcore.toggleSourceEdit(sourceEditMode);
25556         
25557         if(this.editorcore.sourceEditMode){
25558             Roo.log('editor - showing textarea');
25559             
25560 //            Roo.log('in');
25561 //            Roo.log(this.syncValue());
25562             this.syncValue();
25563             this.inputEl().removeClass(['hide', 'x-hidden']);
25564             this.inputEl().dom.removeAttribute('tabIndex');
25565             this.inputEl().focus();
25566         }else{
25567             Roo.log('editor - hiding textarea');
25568 //            Roo.log('out')
25569 //            Roo.log(this.pushValue()); 
25570             this.pushValue();
25571             
25572             this.inputEl().addClass(['hide', 'x-hidden']);
25573             this.inputEl().dom.setAttribute('tabIndex', -1);
25574             //this.deferFocus();
25575         }
25576          
25577         if(this.resizable){
25578             this.setSize(this.wrap.getSize());
25579         }
25580         
25581         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25582     },
25583  
25584     // private (for BoxComponent)
25585     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25586
25587     // private (for BoxComponent)
25588     getResizeEl : function(){
25589         return this.wrap;
25590     },
25591
25592     // private (for BoxComponent)
25593     getPositionEl : function(){
25594         return this.wrap;
25595     },
25596
25597     // private
25598     initEvents : function(){
25599         this.originalValue = this.getValue();
25600     },
25601
25602 //    /**
25603 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25604 //     * @method
25605 //     */
25606 //    markInvalid : Roo.emptyFn,
25607 //    /**
25608 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25609 //     * @method
25610 //     */
25611 //    clearInvalid : Roo.emptyFn,
25612
25613     setValue : function(v){
25614         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25615         this.editorcore.pushValue();
25616     },
25617
25618      
25619     // private
25620     deferFocus : function(){
25621         this.focus.defer(10, this);
25622     },
25623
25624     // doc'ed in Field
25625     focus : function(){
25626         this.editorcore.focus();
25627         
25628     },
25629       
25630
25631     // private
25632     onDestroy : function(){
25633         
25634         
25635         
25636         if(this.rendered){
25637             
25638             for (var i =0; i < this.toolbars.length;i++) {
25639                 // fixme - ask toolbars for heights?
25640                 this.toolbars[i].onDestroy();
25641             }
25642             
25643             this.wrap.dom.innerHTML = '';
25644             this.wrap.remove();
25645         }
25646     },
25647
25648     // private
25649     onFirstFocus : function(){
25650         //Roo.log("onFirstFocus");
25651         this.editorcore.onFirstFocus();
25652          for (var i =0; i < this.toolbars.length;i++) {
25653             this.toolbars[i].onFirstFocus();
25654         }
25655         
25656     },
25657     
25658     // private
25659     syncValue : function()
25660     {   
25661         this.editorcore.syncValue();
25662     },
25663     
25664     pushValue : function()
25665     {   
25666         this.editorcore.pushValue();
25667     }
25668      
25669     
25670     // hide stuff that is not compatible
25671     /**
25672      * @event blur
25673      * @hide
25674      */
25675     /**
25676      * @event change
25677      * @hide
25678      */
25679     /**
25680      * @event focus
25681      * @hide
25682      */
25683     /**
25684      * @event specialkey
25685      * @hide
25686      */
25687     /**
25688      * @cfg {String} fieldClass @hide
25689      */
25690     /**
25691      * @cfg {String} focusClass @hide
25692      */
25693     /**
25694      * @cfg {String} autoCreate @hide
25695      */
25696     /**
25697      * @cfg {String} inputType @hide
25698      */
25699      
25700     /**
25701      * @cfg {String} invalidText @hide
25702      */
25703     /**
25704      * @cfg {String} msgFx @hide
25705      */
25706     /**
25707      * @cfg {String} validateOnBlur @hide
25708      */
25709 });
25710  
25711     
25712    
25713    
25714    
25715       
25716 Roo.namespace('Roo.bootstrap.htmleditor');
25717 /**
25718  * @class Roo.bootstrap.HtmlEditorToolbar1
25719  * Basic Toolbar
25720  * 
25721  * @example
25722  * Usage:
25723  *
25724  new Roo.bootstrap.HtmlEditor({
25725     ....
25726     toolbars : [
25727         new Roo.bootstrap.HtmlEditorToolbar1({
25728             disable : { fonts: 1 , format: 1, ..., ... , ...],
25729             btns : [ .... ]
25730         })
25731     }
25732      
25733  * 
25734  * @cfg {Object} disable List of elements to disable..
25735  * @cfg {Array} btns List of additional buttons.
25736  * 
25737  * 
25738  * NEEDS Extra CSS? 
25739  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25740  */
25741  
25742 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25743 {
25744     
25745     Roo.apply(this, config);
25746     
25747     // default disabled, based on 'good practice'..
25748     this.disable = this.disable || {};
25749     Roo.applyIf(this.disable, {
25750         fontSize : true,
25751         colors : true,
25752         specialElements : true
25753     });
25754     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25755     
25756     this.editor = config.editor;
25757     this.editorcore = config.editor.editorcore;
25758     
25759     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25760     
25761     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25762     // dont call parent... till later.
25763 }
25764 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
25765      
25766     bar : true,
25767     
25768     editor : false,
25769     editorcore : false,
25770     
25771     
25772     formats : [
25773         "p" ,  
25774         "h1","h2","h3","h4","h5","h6", 
25775         "pre", "code", 
25776         "abbr", "acronym", "address", "cite", "samp", "var",
25777         'div','span'
25778     ],
25779     
25780     onRender : function(ct, position)
25781     {
25782        // Roo.log("Call onRender: " + this.xtype);
25783         
25784        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25785        Roo.log(this.el);
25786        this.el.dom.style.marginBottom = '0';
25787        var _this = this;
25788        var editorcore = this.editorcore;
25789        var editor= this.editor;
25790        
25791        var children = [];
25792        var btn = function(id,cmd , toggle, handler, html){
25793        
25794             var  event = toggle ? 'toggle' : 'click';
25795        
25796             var a = {
25797                 size : 'sm',
25798                 xtype: 'Button',
25799                 xns: Roo.bootstrap,
25800                 //glyphicon : id,
25801                 fa: id,
25802                 cmd : id || cmd,
25803                 enableToggle:toggle !== false,
25804                 html : html || '',
25805                 pressed : toggle ? false : null,
25806                 listeners : {}
25807             };
25808             a.listeners[toggle ? 'toggle' : 'click'] = function() {
25809                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
25810             };
25811             children.push(a);
25812             return a;
25813        }
25814        
25815     //    var cb_box = function...
25816         
25817         var style = {
25818                 xtype: 'Button',
25819                 size : 'sm',
25820                 xns: Roo.bootstrap,
25821                 fa : 'font',
25822                 //html : 'submit'
25823                 menu : {
25824                     xtype: 'Menu',
25825                     xns: Roo.bootstrap,
25826                     items:  []
25827                 }
25828         };
25829         Roo.each(this.formats, function(f) {
25830             style.menu.items.push({
25831                 xtype :'MenuItem',
25832                 xns: Roo.bootstrap,
25833                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25834                 tagname : f,
25835                 listeners : {
25836                     click : function()
25837                     {
25838                         editorcore.insertTag(this.tagname);
25839                         editor.focus();
25840                     }
25841                 }
25842                 
25843             });
25844         });
25845         children.push(style);   
25846         
25847         btn('bold',false,true);
25848         btn('italic',false,true);
25849         btn('align-left', 'justifyleft',true);
25850         btn('align-center', 'justifycenter',true);
25851         btn('align-right' , 'justifyright',true);
25852         btn('link', false, false, function(btn) {
25853             //Roo.log("create link?");
25854             var url = prompt(this.createLinkText, this.defaultLinkValue);
25855             if(url && url != 'http:/'+'/'){
25856                 this.editorcore.relayCmd('createlink', url);
25857             }
25858         }),
25859         btn('list','insertunorderedlist',true);
25860         btn('pencil', false,true, function(btn){
25861                 Roo.log(this);
25862                 this.toggleSourceEdit(btn.pressed);
25863         });
25864         
25865         if (this.editor.btns.length > 0) {
25866             for (var i = 0; i<this.editor.btns.length; i++) {
25867                 children.push(this.editor.btns[i]);
25868             }
25869         }
25870         
25871         /*
25872         var cog = {
25873                 xtype: 'Button',
25874                 size : 'sm',
25875                 xns: Roo.bootstrap,
25876                 glyphicon : 'cog',
25877                 //html : 'submit'
25878                 menu : {
25879                     xtype: 'Menu',
25880                     xns: Roo.bootstrap,
25881                     items:  []
25882                 }
25883         };
25884         
25885         cog.menu.items.push({
25886             xtype :'MenuItem',
25887             xns: Roo.bootstrap,
25888             html : Clean styles,
25889             tagname : f,
25890             listeners : {
25891                 click : function()
25892                 {
25893                     editorcore.insertTag(this.tagname);
25894                     editor.focus();
25895                 }
25896             }
25897             
25898         });
25899        */
25900         
25901          
25902        this.xtype = 'NavSimplebar';
25903         
25904         for(var i=0;i< children.length;i++) {
25905             
25906             this.buttons.add(this.addxtypeChild(children[i]));
25907             
25908         }
25909         
25910         editor.on('editorevent', this.updateToolbar, this);
25911     },
25912     onBtnClick : function(id)
25913     {
25914        this.editorcore.relayCmd(id);
25915        this.editorcore.focus();
25916     },
25917     
25918     /**
25919      * Protected method that will not generally be called directly. It triggers
25920      * a toolbar update by reading the markup state of the current selection in the editor.
25921      */
25922     updateToolbar: function(){
25923
25924         if(!this.editorcore.activated){
25925             this.editor.onFirstFocus(); // is this neeed?
25926             return;
25927         }
25928
25929         var btns = this.buttons; 
25930         var doc = this.editorcore.doc;
25931         btns.get('bold').setActive(doc.queryCommandState('bold'));
25932         btns.get('italic').setActive(doc.queryCommandState('italic'));
25933         //btns.get('underline').setActive(doc.queryCommandState('underline'));
25934         
25935         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
25936         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
25937         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
25938         
25939         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
25940         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
25941          /*
25942         
25943         var ans = this.editorcore.getAllAncestors();
25944         if (this.formatCombo) {
25945             
25946             
25947             var store = this.formatCombo.store;
25948             this.formatCombo.setValue("");
25949             for (var i =0; i < ans.length;i++) {
25950                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25951                     // select it..
25952                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25953                     break;
25954                 }
25955             }
25956         }
25957         
25958         
25959         
25960         // hides menus... - so this cant be on a menu...
25961         Roo.bootstrap.MenuMgr.hideAll();
25962         */
25963         Roo.bootstrap.MenuMgr.hideAll();
25964         //this.editorsyncValue();
25965     },
25966     onFirstFocus: function() {
25967         this.buttons.each(function(item){
25968            item.enable();
25969         });
25970     },
25971     toggleSourceEdit : function(sourceEditMode){
25972         
25973           
25974         if(sourceEditMode){
25975             Roo.log("disabling buttons");
25976            this.buttons.each( function(item){
25977                 if(item.cmd != 'pencil'){
25978                     item.disable();
25979                 }
25980             });
25981           
25982         }else{
25983             Roo.log("enabling buttons");
25984             if(this.editorcore.initialized){
25985                 this.buttons.each( function(item){
25986                     item.enable();
25987                 });
25988             }
25989             
25990         }
25991         Roo.log("calling toggole on editor");
25992         // tell the editor that it's been pressed..
25993         this.editor.toggleSourceEdit(sourceEditMode);
25994        
25995     }
25996 });
25997
25998
25999
26000
26001
26002 /**
26003  * @class Roo.bootstrap.Table.AbstractSelectionModel
26004  * @extends Roo.util.Observable
26005  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26006  * implemented by descendant classes.  This class should not be directly instantiated.
26007  * @constructor
26008  */
26009 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26010     this.locked = false;
26011     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26012 };
26013
26014
26015 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26016     /** @ignore Called by the grid automatically. Do not call directly. */
26017     init : function(grid){
26018         this.grid = grid;
26019         this.initEvents();
26020     },
26021
26022     /**
26023      * Locks the selections.
26024      */
26025     lock : function(){
26026         this.locked = true;
26027     },
26028
26029     /**
26030      * Unlocks the selections.
26031      */
26032     unlock : function(){
26033         this.locked = false;
26034     },
26035
26036     /**
26037      * Returns true if the selections are locked.
26038      * @return {Boolean}
26039      */
26040     isLocked : function(){
26041         return this.locked;
26042     },
26043     
26044     
26045     initEvents : function ()
26046     {
26047         
26048     }
26049 });
26050 /**
26051  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26052  * @class Roo.bootstrap.Table.RowSelectionModel
26053  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26054  * It supports multiple selections and keyboard selection/navigation. 
26055  * @constructor
26056  * @param {Object} config
26057  */
26058
26059 Roo.bootstrap.Table.RowSelectionModel = function(config){
26060     Roo.apply(this, config);
26061     this.selections = new Roo.util.MixedCollection(false, function(o){
26062         return o.id;
26063     });
26064
26065     this.last = false;
26066     this.lastActive = false;
26067
26068     this.addEvents({
26069         /**
26070              * @event selectionchange
26071              * Fires when the selection changes
26072              * @param {SelectionModel} this
26073              */
26074             "selectionchange" : true,
26075         /**
26076              * @event afterselectionchange
26077              * Fires after the selection changes (eg. by key press or clicking)
26078              * @param {SelectionModel} this
26079              */
26080             "afterselectionchange" : true,
26081         /**
26082              * @event beforerowselect
26083              * Fires when a row is selected being selected, return false to cancel.
26084              * @param {SelectionModel} this
26085              * @param {Number} rowIndex The selected index
26086              * @param {Boolean} keepExisting False if other selections will be cleared
26087              */
26088             "beforerowselect" : true,
26089         /**
26090              * @event rowselect
26091              * Fires when a row is selected.
26092              * @param {SelectionModel} this
26093              * @param {Number} rowIndex The selected index
26094              * @param {Roo.data.Record} r The record
26095              */
26096             "rowselect" : true,
26097         /**
26098              * @event rowdeselect
26099              * Fires when a row is deselected.
26100              * @param {SelectionModel} this
26101              * @param {Number} rowIndex The selected index
26102              */
26103         "rowdeselect" : true
26104     });
26105     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26106     this.locked = false;
26107  };
26108
26109 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26110     /**
26111      * @cfg {Boolean} singleSelect
26112      * True to allow selection of only one row at a time (defaults to false)
26113      */
26114     singleSelect : false,
26115
26116     // private
26117     initEvents : function()
26118     {
26119
26120         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26121         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26122         //}else{ // allow click to work like normal
26123          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26124         //}
26125         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26126         this.grid.on("rowclick", this.handleMouseDown, this);
26127         
26128         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26129             "up" : function(e){
26130                 if(!e.shiftKey){
26131                     this.selectPrevious(e.shiftKey);
26132                 }else if(this.last !== false && this.lastActive !== false){
26133                     var last = this.last;
26134                     this.selectRange(this.last,  this.lastActive-1);
26135                     this.grid.getView().focusRow(this.lastActive);
26136                     if(last !== false){
26137                         this.last = last;
26138                     }
26139                 }else{
26140                     this.selectFirstRow();
26141                 }
26142                 this.fireEvent("afterselectionchange", this);
26143             },
26144             "down" : function(e){
26145                 if(!e.shiftKey){
26146                     this.selectNext(e.shiftKey);
26147                 }else if(this.last !== false && this.lastActive !== false){
26148                     var last = this.last;
26149                     this.selectRange(this.last,  this.lastActive+1);
26150                     this.grid.getView().focusRow(this.lastActive);
26151                     if(last !== false){
26152                         this.last = last;
26153                     }
26154                 }else{
26155                     this.selectFirstRow();
26156                 }
26157                 this.fireEvent("afterselectionchange", this);
26158             },
26159             scope: this
26160         });
26161         this.grid.store.on('load', function(){
26162             this.selections.clear();
26163         },this);
26164         /*
26165         var view = this.grid.view;
26166         view.on("refresh", this.onRefresh, this);
26167         view.on("rowupdated", this.onRowUpdated, this);
26168         view.on("rowremoved", this.onRemove, this);
26169         */
26170     },
26171
26172     // private
26173     onRefresh : function()
26174     {
26175         var ds = this.grid.store, i, v = this.grid.view;
26176         var s = this.selections;
26177         s.each(function(r){
26178             if((i = ds.indexOfId(r.id)) != -1){
26179                 v.onRowSelect(i);
26180             }else{
26181                 s.remove(r);
26182             }
26183         });
26184     },
26185
26186     // private
26187     onRemove : function(v, index, r){
26188         this.selections.remove(r);
26189     },
26190
26191     // private
26192     onRowUpdated : function(v, index, r){
26193         if(this.isSelected(r)){
26194             v.onRowSelect(index);
26195         }
26196     },
26197
26198     /**
26199      * Select records.
26200      * @param {Array} records The records to select
26201      * @param {Boolean} keepExisting (optional) True to keep existing selections
26202      */
26203     selectRecords : function(records, keepExisting)
26204     {
26205         if(!keepExisting){
26206             this.clearSelections();
26207         }
26208             var ds = this.grid.store;
26209         for(var i = 0, len = records.length; i < len; i++){
26210             this.selectRow(ds.indexOf(records[i]), true);
26211         }
26212     },
26213
26214     /**
26215      * Gets the number of selected rows.
26216      * @return {Number}
26217      */
26218     getCount : function(){
26219         return this.selections.length;
26220     },
26221
26222     /**
26223      * Selects the first row in the grid.
26224      */
26225     selectFirstRow : function(){
26226         this.selectRow(0);
26227     },
26228
26229     /**
26230      * Select the last row.
26231      * @param {Boolean} keepExisting (optional) True to keep existing selections
26232      */
26233     selectLastRow : function(keepExisting){
26234         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26235         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26236     },
26237
26238     /**
26239      * Selects the row immediately following the last selected row.
26240      * @param {Boolean} keepExisting (optional) True to keep existing selections
26241      */
26242     selectNext : function(keepExisting)
26243     {
26244             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26245             this.selectRow(this.last+1, keepExisting);
26246             this.grid.getView().focusRow(this.last);
26247         }
26248     },
26249
26250     /**
26251      * Selects the row that precedes the last selected row.
26252      * @param {Boolean} keepExisting (optional) True to keep existing selections
26253      */
26254     selectPrevious : function(keepExisting){
26255         if(this.last){
26256             this.selectRow(this.last-1, keepExisting);
26257             this.grid.getView().focusRow(this.last);
26258         }
26259     },
26260
26261     /**
26262      * Returns the selected records
26263      * @return {Array} Array of selected records
26264      */
26265     getSelections : function(){
26266         return [].concat(this.selections.items);
26267     },
26268
26269     /**
26270      * Returns the first selected record.
26271      * @return {Record}
26272      */
26273     getSelected : function(){
26274         return this.selections.itemAt(0);
26275     },
26276
26277
26278     /**
26279      * Clears all selections.
26280      */
26281     clearSelections : function(fast)
26282     {
26283         if(this.locked) {
26284             return;
26285         }
26286         if(fast !== true){
26287                 var ds = this.grid.store;
26288             var s = this.selections;
26289             s.each(function(r){
26290                 this.deselectRow(ds.indexOfId(r.id));
26291             }, this);
26292             s.clear();
26293         }else{
26294             this.selections.clear();
26295         }
26296         this.last = false;
26297     },
26298
26299
26300     /**
26301      * Selects all rows.
26302      */
26303     selectAll : function(){
26304         if(this.locked) {
26305             return;
26306         }
26307         this.selections.clear();
26308         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26309             this.selectRow(i, true);
26310         }
26311     },
26312
26313     /**
26314      * Returns True if there is a selection.
26315      * @return {Boolean}
26316      */
26317     hasSelection : function(){
26318         return this.selections.length > 0;
26319     },
26320
26321     /**
26322      * Returns True if the specified row is selected.
26323      * @param {Number/Record} record The record or index of the record to check
26324      * @return {Boolean}
26325      */
26326     isSelected : function(index){
26327             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26328         return (r && this.selections.key(r.id) ? true : false);
26329     },
26330
26331     /**
26332      * Returns True if the specified record id is selected.
26333      * @param {String} id The id of record to check
26334      * @return {Boolean}
26335      */
26336     isIdSelected : function(id){
26337         return (this.selections.key(id) ? true : false);
26338     },
26339
26340
26341     // private
26342     handleMouseDBClick : function(e, t){
26343         
26344     },
26345     // private
26346     handleMouseDown : function(e, t)
26347     {
26348             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26349         if(this.isLocked() || rowIndex < 0 ){
26350             return;
26351         };
26352         if(e.shiftKey && this.last !== false){
26353             var last = this.last;
26354             this.selectRange(last, rowIndex, e.ctrlKey);
26355             this.last = last; // reset the last
26356             t.focus();
26357     
26358         }else{
26359             var isSelected = this.isSelected(rowIndex);
26360             //Roo.log("select row:" + rowIndex);
26361             if(isSelected){
26362                 this.deselectRow(rowIndex);
26363             } else {
26364                         this.selectRow(rowIndex, true);
26365             }
26366     
26367             /*
26368                 if(e.button !== 0 && isSelected){
26369                 alert('rowIndex 2: ' + rowIndex);
26370                     view.focusRow(rowIndex);
26371                 }else if(e.ctrlKey && isSelected){
26372                     this.deselectRow(rowIndex);
26373                 }else if(!isSelected){
26374                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26375                     view.focusRow(rowIndex);
26376                 }
26377             */
26378         }
26379         this.fireEvent("afterselectionchange", this);
26380     },
26381     // private
26382     handleDragableRowClick :  function(grid, rowIndex, e) 
26383     {
26384         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26385             this.selectRow(rowIndex, false);
26386             grid.view.focusRow(rowIndex);
26387              this.fireEvent("afterselectionchange", this);
26388         }
26389     },
26390     
26391     /**
26392      * Selects multiple rows.
26393      * @param {Array} rows Array of the indexes of the row to select
26394      * @param {Boolean} keepExisting (optional) True to keep existing selections
26395      */
26396     selectRows : function(rows, keepExisting){
26397         if(!keepExisting){
26398             this.clearSelections();
26399         }
26400         for(var i = 0, len = rows.length; i < len; i++){
26401             this.selectRow(rows[i], true);
26402         }
26403     },
26404
26405     /**
26406      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26407      * @param {Number} startRow The index of the first row in the range
26408      * @param {Number} endRow The index of the last row in the range
26409      * @param {Boolean} keepExisting (optional) True to retain existing selections
26410      */
26411     selectRange : function(startRow, endRow, keepExisting){
26412         if(this.locked) {
26413             return;
26414         }
26415         if(!keepExisting){
26416             this.clearSelections();
26417         }
26418         if(startRow <= endRow){
26419             for(var i = startRow; i <= endRow; i++){
26420                 this.selectRow(i, true);
26421             }
26422         }else{
26423             for(var i = startRow; i >= endRow; i--){
26424                 this.selectRow(i, true);
26425             }
26426         }
26427     },
26428
26429     /**
26430      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26431      * @param {Number} startRow The index of the first row in the range
26432      * @param {Number} endRow The index of the last row in the range
26433      */
26434     deselectRange : function(startRow, endRow, preventViewNotify){
26435         if(this.locked) {
26436             return;
26437         }
26438         for(var i = startRow; i <= endRow; i++){
26439             this.deselectRow(i, preventViewNotify);
26440         }
26441     },
26442
26443     /**
26444      * Selects a row.
26445      * @param {Number} row The index of the row to select
26446      * @param {Boolean} keepExisting (optional) True to keep existing selections
26447      */
26448     selectRow : function(index, keepExisting, preventViewNotify)
26449     {
26450             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26451             return;
26452         }
26453         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26454             if(!keepExisting || this.singleSelect){
26455                 this.clearSelections();
26456             }
26457             
26458             var r = this.grid.store.getAt(index);
26459             //console.log('selectRow - record id :' + r.id);
26460             
26461             this.selections.add(r);
26462             this.last = this.lastActive = index;
26463             if(!preventViewNotify){
26464                 var proxy = new Roo.Element(
26465                                 this.grid.getRowDom(index)
26466                 );
26467                 proxy.addClass('bg-info info');
26468             }
26469             this.fireEvent("rowselect", this, index, r);
26470             this.fireEvent("selectionchange", this);
26471         }
26472     },
26473
26474     /**
26475      * Deselects a row.
26476      * @param {Number} row The index of the row to deselect
26477      */
26478     deselectRow : function(index, preventViewNotify)
26479     {
26480         if(this.locked) {
26481             return;
26482         }
26483         if(this.last == index){
26484             this.last = false;
26485         }
26486         if(this.lastActive == index){
26487             this.lastActive = false;
26488         }
26489         
26490         var r = this.grid.store.getAt(index);
26491         if (!r) {
26492             return;
26493         }
26494         
26495         this.selections.remove(r);
26496         //.console.log('deselectRow - record id :' + r.id);
26497         if(!preventViewNotify){
26498         
26499             var proxy = new Roo.Element(
26500                 this.grid.getRowDom(index)
26501             );
26502             proxy.removeClass('bg-info info');
26503         }
26504         this.fireEvent("rowdeselect", this, index);
26505         this.fireEvent("selectionchange", this);
26506     },
26507
26508     // private
26509     restoreLast : function(){
26510         if(this._last){
26511             this.last = this._last;
26512         }
26513     },
26514
26515     // private
26516     acceptsNav : function(row, col, cm){
26517         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26518     },
26519
26520     // private
26521     onEditorKey : function(field, e){
26522         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26523         if(k == e.TAB){
26524             e.stopEvent();
26525             ed.completeEdit();
26526             if(e.shiftKey){
26527                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26528             }else{
26529                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26530             }
26531         }else if(k == e.ENTER && !e.ctrlKey){
26532             e.stopEvent();
26533             ed.completeEdit();
26534             if(e.shiftKey){
26535                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26536             }else{
26537                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26538             }
26539         }else if(k == e.ESC){
26540             ed.cancelEdit();
26541         }
26542         if(newCell){
26543             g.startEditing(newCell[0], newCell[1]);
26544         }
26545     }
26546 });
26547 /*
26548  * Based on:
26549  * Ext JS Library 1.1.1
26550  * Copyright(c) 2006-2007, Ext JS, LLC.
26551  *
26552  * Originally Released Under LGPL - original licence link has changed is not relivant.
26553  *
26554  * Fork - LGPL
26555  * <script type="text/javascript">
26556  */
26557  
26558 /**
26559  * @class Roo.bootstrap.PagingToolbar
26560  * @extends Roo.bootstrap.NavSimplebar
26561  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26562  * @constructor
26563  * Create a new PagingToolbar
26564  * @param {Object} config The config object
26565  * @param {Roo.data.Store} store
26566  */
26567 Roo.bootstrap.PagingToolbar = function(config)
26568 {
26569     // old args format still supported... - xtype is prefered..
26570         // created from xtype...
26571     
26572     this.ds = config.dataSource;
26573     
26574     if (config.store && !this.ds) {
26575         this.store= Roo.factory(config.store, Roo.data);
26576         this.ds = this.store;
26577         this.ds.xmodule = this.xmodule || false;
26578     }
26579     
26580     this.toolbarItems = [];
26581     if (config.items) {
26582         this.toolbarItems = config.items;
26583     }
26584     
26585     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26586     
26587     this.cursor = 0;
26588     
26589     if (this.ds) { 
26590         this.bind(this.ds);
26591     }
26592     
26593     if (Roo.bootstrap.version == 4) {
26594         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26595     } else {
26596         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26597     }
26598     
26599 };
26600
26601 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26602     /**
26603      * @cfg {Roo.data.Store} dataSource
26604      * The underlying data store providing the paged data
26605      */
26606     /**
26607      * @cfg {String/HTMLElement/Element} container
26608      * container The id or element that will contain the toolbar
26609      */
26610     /**
26611      * @cfg {Boolean} displayInfo
26612      * True to display the displayMsg (defaults to false)
26613      */
26614     /**
26615      * @cfg {Number} pageSize
26616      * The number of records to display per page (defaults to 20)
26617      */
26618     pageSize: 20,
26619     /**
26620      * @cfg {String} displayMsg
26621      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26622      */
26623     displayMsg : 'Displaying {0} - {1} of {2}',
26624     /**
26625      * @cfg {String} emptyMsg
26626      * The message to display when no records are found (defaults to "No data to display")
26627      */
26628     emptyMsg : 'No data to display',
26629     /**
26630      * Customizable piece of the default paging text (defaults to "Page")
26631      * @type String
26632      */
26633     beforePageText : "Page",
26634     /**
26635      * Customizable piece of the default paging text (defaults to "of %0")
26636      * @type String
26637      */
26638     afterPageText : "of {0}",
26639     /**
26640      * Customizable piece of the default paging text (defaults to "First Page")
26641      * @type String
26642      */
26643     firstText : "First Page",
26644     /**
26645      * Customizable piece of the default paging text (defaults to "Previous Page")
26646      * @type String
26647      */
26648     prevText : "Previous Page",
26649     /**
26650      * Customizable piece of the default paging text (defaults to "Next Page")
26651      * @type String
26652      */
26653     nextText : "Next Page",
26654     /**
26655      * Customizable piece of the default paging text (defaults to "Last Page")
26656      * @type String
26657      */
26658     lastText : "Last Page",
26659     /**
26660      * Customizable piece of the default paging text (defaults to "Refresh")
26661      * @type String
26662      */
26663     refreshText : "Refresh",
26664
26665     buttons : false,
26666     // private
26667     onRender : function(ct, position) 
26668     {
26669         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26670         this.navgroup.parentId = this.id;
26671         this.navgroup.onRender(this.el, null);
26672         // add the buttons to the navgroup
26673         
26674         if(this.displayInfo){
26675             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26676             this.displayEl = this.el.select('.x-paging-info', true).first();
26677 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26678 //            this.displayEl = navel.el.select('span',true).first();
26679         }
26680         
26681         var _this = this;
26682         
26683         if(this.buttons){
26684             Roo.each(_this.buttons, function(e){ // this might need to use render????
26685                Roo.factory(e).render(_this.el);
26686             });
26687         }
26688             
26689         Roo.each(_this.toolbarItems, function(e) {
26690             _this.navgroup.addItem(e);
26691         });
26692         
26693         
26694         this.first = this.navgroup.addItem({
26695             tooltip: this.firstText,
26696             cls: "prev btn-outline-secondary",
26697             html : ' <i class="fa fa-step-backward"></i>',
26698             disabled: true,
26699             preventDefault: true,
26700             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26701         });
26702         
26703         this.prev =  this.navgroup.addItem({
26704             tooltip: this.prevText,
26705             cls: "prev btn-outline-secondary",
26706             html : ' <i class="fa fa-backward"></i>',
26707             disabled: true,
26708             preventDefault: true,
26709             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
26710         });
26711     //this.addSeparator();
26712         
26713         
26714         var field = this.navgroup.addItem( {
26715             tagtype : 'span',
26716             cls : 'x-paging-position  btn-outline-secondary',
26717              disabled: true,
26718             html : this.beforePageText  +
26719                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26720                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
26721          } ); //?? escaped?
26722         
26723         this.field = field.el.select('input', true).first();
26724         this.field.on("keydown", this.onPagingKeydown, this);
26725         this.field.on("focus", function(){this.dom.select();});
26726     
26727     
26728         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
26729         //this.field.setHeight(18);
26730         //this.addSeparator();
26731         this.next = this.navgroup.addItem({
26732             tooltip: this.nextText,
26733             cls: "next btn-outline-secondary",
26734             html : ' <i class="fa fa-forward"></i>',
26735             disabled: true,
26736             preventDefault: true,
26737             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
26738         });
26739         this.last = this.navgroup.addItem({
26740             tooltip: this.lastText,
26741             html : ' <i class="fa fa-step-forward"></i>',
26742             cls: "next btn-outline-secondary",
26743             disabled: true,
26744             preventDefault: true,
26745             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
26746         });
26747     //this.addSeparator();
26748         this.loading = this.navgroup.addItem({
26749             tooltip: this.refreshText,
26750             cls: "btn-outline-secondary",
26751             html : ' <i class="fa fa-refresh"></i>',
26752             preventDefault: true,
26753             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26754         });
26755         
26756     },
26757
26758     // private
26759     updateInfo : function(){
26760         if(this.displayEl){
26761             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26762             var msg = count == 0 ?
26763                 this.emptyMsg :
26764                 String.format(
26765                     this.displayMsg,
26766                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
26767                 );
26768             this.displayEl.update(msg);
26769         }
26770     },
26771
26772     // private
26773     onLoad : function(ds, r, o)
26774     {
26775         this.cursor = o.params.start ? o.params.start : 0;
26776         
26777         var d = this.getPageData(),
26778             ap = d.activePage,
26779             ps = d.pages;
26780         
26781         
26782         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26783         this.field.dom.value = ap;
26784         this.first.setDisabled(ap == 1);
26785         this.prev.setDisabled(ap == 1);
26786         this.next.setDisabled(ap == ps);
26787         this.last.setDisabled(ap == ps);
26788         this.loading.enable();
26789         this.updateInfo();
26790     },
26791
26792     // private
26793     getPageData : function(){
26794         var total = this.ds.getTotalCount();
26795         return {
26796             total : total,
26797             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26798             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
26799         };
26800     },
26801
26802     // private
26803     onLoadError : function(){
26804         this.loading.enable();
26805     },
26806
26807     // private
26808     onPagingKeydown : function(e){
26809         var k = e.getKey();
26810         var d = this.getPageData();
26811         if(k == e.RETURN){
26812             var v = this.field.dom.value, pageNum;
26813             if(!v || isNaN(pageNum = parseInt(v, 10))){
26814                 this.field.dom.value = d.activePage;
26815                 return;
26816             }
26817             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
26818             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26819             e.stopEvent();
26820         }
26821         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))
26822         {
26823           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
26824           this.field.dom.value = pageNum;
26825           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
26826           e.stopEvent();
26827         }
26828         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
26829         {
26830           var v = this.field.dom.value, pageNum; 
26831           var increment = (e.shiftKey) ? 10 : 1;
26832           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
26833                 increment *= -1;
26834           }
26835           if(!v || isNaN(pageNum = parseInt(v, 10))) {
26836             this.field.dom.value = d.activePage;
26837             return;
26838           }
26839           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
26840           {
26841             this.field.dom.value = parseInt(v, 10) + increment;
26842             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
26843             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26844           }
26845           e.stopEvent();
26846         }
26847     },
26848
26849     // private
26850     beforeLoad : function(){
26851         if(this.loading){
26852             this.loading.disable();
26853         }
26854     },
26855
26856     // private
26857     onClick : function(which){
26858         
26859         var ds = this.ds;
26860         if (!ds) {
26861             return;
26862         }
26863         
26864         switch(which){
26865             case "first":
26866                 ds.load({params:{start: 0, limit: this.pageSize}});
26867             break;
26868             case "prev":
26869                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
26870             break;
26871             case "next":
26872                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
26873             break;
26874             case "last":
26875                 var total = ds.getTotalCount();
26876                 var extra = total % this.pageSize;
26877                 var lastStart = extra ? (total - extra) : total-this.pageSize;
26878                 ds.load({params:{start: lastStart, limit: this.pageSize}});
26879             break;
26880             case "refresh":
26881                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
26882             break;
26883         }
26884     },
26885
26886     /**
26887      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
26888      * @param {Roo.data.Store} store The data store to unbind
26889      */
26890     unbind : function(ds){
26891         ds.un("beforeload", this.beforeLoad, this);
26892         ds.un("load", this.onLoad, this);
26893         ds.un("loadexception", this.onLoadError, this);
26894         ds.un("remove", this.updateInfo, this);
26895         ds.un("add", this.updateInfo, this);
26896         this.ds = undefined;
26897     },
26898
26899     /**
26900      * Binds the paging toolbar to the specified {@link Roo.data.Store}
26901      * @param {Roo.data.Store} store The data store to bind
26902      */
26903     bind : function(ds){
26904         ds.on("beforeload", this.beforeLoad, this);
26905         ds.on("load", this.onLoad, this);
26906         ds.on("loadexception", this.onLoadError, this);
26907         ds.on("remove", this.updateInfo, this);
26908         ds.on("add", this.updateInfo, this);
26909         this.ds = ds;
26910     }
26911 });/*
26912  * - LGPL
26913  *
26914  * element
26915  * 
26916  */
26917
26918 /**
26919  * @class Roo.bootstrap.MessageBar
26920  * @extends Roo.bootstrap.Component
26921  * Bootstrap MessageBar class
26922  * @cfg {String} html contents of the MessageBar
26923  * @cfg {String} weight (info | success | warning | danger) default info
26924  * @cfg {String} beforeClass insert the bar before the given class
26925  * @cfg {Boolean} closable (true | false) default false
26926  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
26927  * 
26928  * @constructor
26929  * Create a new Element
26930  * @param {Object} config The config object
26931  */
26932
26933 Roo.bootstrap.MessageBar = function(config){
26934     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
26935 };
26936
26937 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
26938     
26939     html: '',
26940     weight: 'info',
26941     closable: false,
26942     fixed: false,
26943     beforeClass: 'bootstrap-sticky-wrap',
26944     
26945     getAutoCreate : function(){
26946         
26947         var cfg = {
26948             tag: 'div',
26949             cls: 'alert alert-dismissable alert-' + this.weight,
26950             cn: [
26951                 {
26952                     tag: 'span',
26953                     cls: 'message',
26954                     html: this.html || ''
26955                 }
26956             ]
26957         };
26958         
26959         if(this.fixed){
26960             cfg.cls += ' alert-messages-fixed';
26961         }
26962         
26963         if(this.closable){
26964             cfg.cn.push({
26965                 tag: 'button',
26966                 cls: 'close',
26967                 html: 'x'
26968             });
26969         }
26970         
26971         return cfg;
26972     },
26973     
26974     onRender : function(ct, position)
26975     {
26976         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
26977         
26978         if(!this.el){
26979             var cfg = Roo.apply({},  this.getAutoCreate());
26980             cfg.id = Roo.id();
26981             
26982             if (this.cls) {
26983                 cfg.cls += ' ' + this.cls;
26984             }
26985             if (this.style) {
26986                 cfg.style = this.style;
26987             }
26988             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
26989             
26990             this.el.setVisibilityMode(Roo.Element.DISPLAY);
26991         }
26992         
26993         this.el.select('>button.close').on('click', this.hide, this);
26994         
26995     },
26996     
26997     show : function()
26998     {
26999         if (!this.rendered) {
27000             this.render();
27001         }
27002         
27003         this.el.show();
27004         
27005         this.fireEvent('show', this);
27006         
27007     },
27008     
27009     hide : function()
27010     {
27011         if (!this.rendered) {
27012             this.render();
27013         }
27014         
27015         this.el.hide();
27016         
27017         this.fireEvent('hide', this);
27018     },
27019     
27020     update : function()
27021     {
27022 //        var e = this.el.dom.firstChild;
27023 //        
27024 //        if(this.closable){
27025 //            e = e.nextSibling;
27026 //        }
27027 //        
27028 //        e.data = this.html || '';
27029
27030         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27031     }
27032    
27033 });
27034
27035  
27036
27037      /*
27038  * - LGPL
27039  *
27040  * Graph
27041  * 
27042  */
27043
27044
27045 /**
27046  * @class Roo.bootstrap.Graph
27047  * @extends Roo.bootstrap.Component
27048  * Bootstrap Graph class
27049 > Prameters
27050  -sm {number} sm 4
27051  -md {number} md 5
27052  @cfg {String} graphtype  bar | vbar | pie
27053  @cfg {number} g_x coodinator | centre x (pie)
27054  @cfg {number} g_y coodinator | centre y (pie)
27055  @cfg {number} g_r radius (pie)
27056  @cfg {number} g_height height of the chart (respected by all elements in the set)
27057  @cfg {number} g_width width of the chart (respected by all elements in the set)
27058  @cfg {Object} title The title of the chart
27059     
27060  -{Array}  values
27061  -opts (object) options for the chart 
27062      o {
27063      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27064      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27065      o vgutter (number)
27066      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.
27067      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27068      o to
27069      o stretch (boolean)
27070      o }
27071  -opts (object) options for the pie
27072      o{
27073      o cut
27074      o startAngle (number)
27075      o endAngle (number)
27076      } 
27077  *
27078  * @constructor
27079  * Create a new Input
27080  * @param {Object} config The config object
27081  */
27082
27083 Roo.bootstrap.Graph = function(config){
27084     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27085     
27086     this.addEvents({
27087         // img events
27088         /**
27089          * @event click
27090          * The img click event for the img.
27091          * @param {Roo.EventObject} e
27092          */
27093         "click" : true
27094     });
27095 };
27096
27097 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27098     
27099     sm: 4,
27100     md: 5,
27101     graphtype: 'bar',
27102     g_height: 250,
27103     g_width: 400,
27104     g_x: 50,
27105     g_y: 50,
27106     g_r: 30,
27107     opts:{
27108         //g_colors: this.colors,
27109         g_type: 'soft',
27110         g_gutter: '20%'
27111
27112     },
27113     title : false,
27114
27115     getAutoCreate : function(){
27116         
27117         var cfg = {
27118             tag: 'div',
27119             html : null
27120         };
27121         
27122         
27123         return  cfg;
27124     },
27125
27126     onRender : function(ct,position){
27127         
27128         
27129         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27130         
27131         if (typeof(Raphael) == 'undefined') {
27132             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27133             return;
27134         }
27135         
27136         this.raphael = Raphael(this.el.dom);
27137         
27138                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27139                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27140                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27141                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27142                 /*
27143                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27144                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27145                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27146                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27147                 
27148                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27149                 r.barchart(330, 10, 300, 220, data1);
27150                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27151                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27152                 */
27153                 
27154                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27155                 // r.barchart(30, 30, 560, 250,  xdata, {
27156                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27157                 //     axis : "0 0 1 1",
27158                 //     axisxlabels :  xdata
27159                 //     //yvalues : cols,
27160                    
27161                 // });
27162 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27163 //        
27164 //        this.load(null,xdata,{
27165 //                axis : "0 0 1 1",
27166 //                axisxlabels :  xdata
27167 //                });
27168
27169     },
27170
27171     load : function(graphtype,xdata,opts)
27172     {
27173         this.raphael.clear();
27174         if(!graphtype) {
27175             graphtype = this.graphtype;
27176         }
27177         if(!opts){
27178             opts = this.opts;
27179         }
27180         var r = this.raphael,
27181             fin = function () {
27182                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27183             },
27184             fout = function () {
27185                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27186             },
27187             pfin = function() {
27188                 this.sector.stop();
27189                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27190
27191                 if (this.label) {
27192                     this.label[0].stop();
27193                     this.label[0].attr({ r: 7.5 });
27194                     this.label[1].attr({ "font-weight": 800 });
27195                 }
27196             },
27197             pfout = function() {
27198                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27199
27200                 if (this.label) {
27201                     this.label[0].animate({ r: 5 }, 500, "bounce");
27202                     this.label[1].attr({ "font-weight": 400 });
27203                 }
27204             };
27205
27206         switch(graphtype){
27207             case 'bar':
27208                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27209                 break;
27210             case 'hbar':
27211                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27212                 break;
27213             case 'pie':
27214 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27215 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27216 //            
27217                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27218                 
27219                 break;
27220
27221         }
27222         
27223         if(this.title){
27224             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27225         }
27226         
27227     },
27228     
27229     setTitle: function(o)
27230     {
27231         this.title = o;
27232     },
27233     
27234     initEvents: function() {
27235         
27236         if(!this.href){
27237             this.el.on('click', this.onClick, this);
27238         }
27239     },
27240     
27241     onClick : function(e)
27242     {
27243         Roo.log('img onclick');
27244         this.fireEvent('click', this, e);
27245     }
27246    
27247 });
27248
27249  
27250 /*
27251  * - LGPL
27252  *
27253  * numberBox
27254  * 
27255  */
27256 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27257
27258 /**
27259  * @class Roo.bootstrap.dash.NumberBox
27260  * @extends Roo.bootstrap.Component
27261  * Bootstrap NumberBox class
27262  * @cfg {String} headline Box headline
27263  * @cfg {String} content Box content
27264  * @cfg {String} icon Box icon
27265  * @cfg {String} footer Footer text
27266  * @cfg {String} fhref Footer href
27267  * 
27268  * @constructor
27269  * Create a new NumberBox
27270  * @param {Object} config The config object
27271  */
27272
27273
27274 Roo.bootstrap.dash.NumberBox = function(config){
27275     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27276     
27277 };
27278
27279 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27280     
27281     headline : '',
27282     content : '',
27283     icon : '',
27284     footer : '',
27285     fhref : '',
27286     ficon : '',
27287     
27288     getAutoCreate : function(){
27289         
27290         var cfg = {
27291             tag : 'div',
27292             cls : 'small-box ',
27293             cn : [
27294                 {
27295                     tag : 'div',
27296                     cls : 'inner',
27297                     cn :[
27298                         {
27299                             tag : 'h3',
27300                             cls : 'roo-headline',
27301                             html : this.headline
27302                         },
27303                         {
27304                             tag : 'p',
27305                             cls : 'roo-content',
27306                             html : this.content
27307                         }
27308                     ]
27309                 }
27310             ]
27311         };
27312         
27313         if(this.icon){
27314             cfg.cn.push({
27315                 tag : 'div',
27316                 cls : 'icon',
27317                 cn :[
27318                     {
27319                         tag : 'i',
27320                         cls : 'ion ' + this.icon
27321                     }
27322                 ]
27323             });
27324         }
27325         
27326         if(this.footer){
27327             var footer = {
27328                 tag : 'a',
27329                 cls : 'small-box-footer',
27330                 href : this.fhref || '#',
27331                 html : this.footer
27332             };
27333             
27334             cfg.cn.push(footer);
27335             
27336         }
27337         
27338         return  cfg;
27339     },
27340
27341     onRender : function(ct,position){
27342         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27343
27344
27345        
27346                 
27347     },
27348
27349     setHeadline: function (value)
27350     {
27351         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27352     },
27353     
27354     setFooter: function (value, href)
27355     {
27356         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27357         
27358         if(href){
27359             this.el.select('a.small-box-footer',true).first().attr('href', href);
27360         }
27361         
27362     },
27363
27364     setContent: function (value)
27365     {
27366         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27367     },
27368
27369     initEvents: function() 
27370     {   
27371         
27372     }
27373     
27374 });
27375
27376  
27377 /*
27378  * - LGPL
27379  *
27380  * TabBox
27381  * 
27382  */
27383 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27384
27385 /**
27386  * @class Roo.bootstrap.dash.TabBox
27387  * @extends Roo.bootstrap.Component
27388  * Bootstrap TabBox class
27389  * @cfg {String} title Title of the TabBox
27390  * @cfg {String} icon Icon of the TabBox
27391  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27392  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27393  * 
27394  * @constructor
27395  * Create a new TabBox
27396  * @param {Object} config The config object
27397  */
27398
27399
27400 Roo.bootstrap.dash.TabBox = function(config){
27401     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27402     this.addEvents({
27403         // raw events
27404         /**
27405          * @event addpane
27406          * When a pane is added
27407          * @param {Roo.bootstrap.dash.TabPane} pane
27408          */
27409         "addpane" : true,
27410         /**
27411          * @event activatepane
27412          * When a pane is activated
27413          * @param {Roo.bootstrap.dash.TabPane} pane
27414          */
27415         "activatepane" : true
27416         
27417          
27418     });
27419     
27420     this.panes = [];
27421 };
27422
27423 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27424
27425     title : '',
27426     icon : false,
27427     showtabs : true,
27428     tabScrollable : false,
27429     
27430     getChildContainer : function()
27431     {
27432         return this.el.select('.tab-content', true).first();
27433     },
27434     
27435     getAutoCreate : function(){
27436         
27437         var header = {
27438             tag: 'li',
27439             cls: 'pull-left header',
27440             html: this.title,
27441             cn : []
27442         };
27443         
27444         if(this.icon){
27445             header.cn.push({
27446                 tag: 'i',
27447                 cls: 'fa ' + this.icon
27448             });
27449         }
27450         
27451         var h = {
27452             tag: 'ul',
27453             cls: 'nav nav-tabs pull-right',
27454             cn: [
27455                 header
27456             ]
27457         };
27458         
27459         if(this.tabScrollable){
27460             h = {
27461                 tag: 'div',
27462                 cls: 'tab-header',
27463                 cn: [
27464                     {
27465                         tag: 'ul',
27466                         cls: 'nav nav-tabs pull-right',
27467                         cn: [
27468                             header
27469                         ]
27470                     }
27471                 ]
27472             };
27473         }
27474         
27475         var cfg = {
27476             tag: 'div',
27477             cls: 'nav-tabs-custom',
27478             cn: [
27479                 h,
27480                 {
27481                     tag: 'div',
27482                     cls: 'tab-content no-padding',
27483                     cn: []
27484                 }
27485             ]
27486         };
27487
27488         return  cfg;
27489     },
27490     initEvents : function()
27491     {
27492         //Roo.log('add add pane handler');
27493         this.on('addpane', this.onAddPane, this);
27494     },
27495      /**
27496      * Updates the box title
27497      * @param {String} html to set the title to.
27498      */
27499     setTitle : function(value)
27500     {
27501         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27502     },
27503     onAddPane : function(pane)
27504     {
27505         this.panes.push(pane);
27506         //Roo.log('addpane');
27507         //Roo.log(pane);
27508         // tabs are rendere left to right..
27509         if(!this.showtabs){
27510             return;
27511         }
27512         
27513         var ctr = this.el.select('.nav-tabs', true).first();
27514          
27515          
27516         var existing = ctr.select('.nav-tab',true);
27517         var qty = existing.getCount();;
27518         
27519         
27520         var tab = ctr.createChild({
27521             tag : 'li',
27522             cls : 'nav-tab' + (qty ? '' : ' active'),
27523             cn : [
27524                 {
27525                     tag : 'a',
27526                     href:'#',
27527                     html : pane.title
27528                 }
27529             ]
27530         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27531         pane.tab = tab;
27532         
27533         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27534         if (!qty) {
27535             pane.el.addClass('active');
27536         }
27537         
27538                 
27539     },
27540     onTabClick : function(ev,un,ob,pane)
27541     {
27542         //Roo.log('tab - prev default');
27543         ev.preventDefault();
27544         
27545         
27546         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27547         pane.tab.addClass('active');
27548         //Roo.log(pane.title);
27549         this.getChildContainer().select('.tab-pane',true).removeClass('active');
27550         // technically we should have a deactivate event.. but maybe add later.
27551         // and it should not de-activate the selected tab...
27552         this.fireEvent('activatepane', pane);
27553         pane.el.addClass('active');
27554         pane.fireEvent('activate');
27555         
27556         
27557     },
27558     
27559     getActivePane : function()
27560     {
27561         var r = false;
27562         Roo.each(this.panes, function(p) {
27563             if(p.el.hasClass('active')){
27564                 r = p;
27565                 return false;
27566             }
27567             
27568             return;
27569         });
27570         
27571         return r;
27572     }
27573     
27574     
27575 });
27576
27577  
27578 /*
27579  * - LGPL
27580  *
27581  * Tab pane
27582  * 
27583  */
27584 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27585 /**
27586  * @class Roo.bootstrap.TabPane
27587  * @extends Roo.bootstrap.Component
27588  * Bootstrap TabPane class
27589  * @cfg {Boolean} active (false | true) Default false
27590  * @cfg {String} title title of panel
27591
27592  * 
27593  * @constructor
27594  * Create a new TabPane
27595  * @param {Object} config The config object
27596  */
27597
27598 Roo.bootstrap.dash.TabPane = function(config){
27599     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27600     
27601     this.addEvents({
27602         // raw events
27603         /**
27604          * @event activate
27605          * When a pane is activated
27606          * @param {Roo.bootstrap.dash.TabPane} pane
27607          */
27608         "activate" : true
27609          
27610     });
27611 };
27612
27613 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
27614     
27615     active : false,
27616     title : '',
27617     
27618     // the tabBox that this is attached to.
27619     tab : false,
27620      
27621     getAutoCreate : function() 
27622     {
27623         var cfg = {
27624             tag: 'div',
27625             cls: 'tab-pane'
27626         };
27627         
27628         if(this.active){
27629             cfg.cls += ' active';
27630         }
27631         
27632         return cfg;
27633     },
27634     initEvents  : function()
27635     {
27636         //Roo.log('trigger add pane handler');
27637         this.parent().fireEvent('addpane', this)
27638     },
27639     
27640      /**
27641      * Updates the tab title 
27642      * @param {String} html to set the title to.
27643      */
27644     setTitle: function(str)
27645     {
27646         if (!this.tab) {
27647             return;
27648         }
27649         this.title = str;
27650         this.tab.select('a', true).first().dom.innerHTML = str;
27651         
27652     }
27653     
27654     
27655     
27656 });
27657
27658  
27659
27660
27661  /*
27662  * - LGPL
27663  *
27664  * menu
27665  * 
27666  */
27667 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27668
27669 /**
27670  * @class Roo.bootstrap.menu.Menu
27671  * @extends Roo.bootstrap.Component
27672  * Bootstrap Menu class - container for Menu
27673  * @cfg {String} html Text of the menu
27674  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27675  * @cfg {String} icon Font awesome icon
27676  * @cfg {String} pos Menu align to (top | bottom) default bottom
27677  * 
27678  * 
27679  * @constructor
27680  * Create a new Menu
27681  * @param {Object} config The config object
27682  */
27683
27684
27685 Roo.bootstrap.menu.Menu = function(config){
27686     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27687     
27688     this.addEvents({
27689         /**
27690          * @event beforeshow
27691          * Fires before this menu is displayed
27692          * @param {Roo.bootstrap.menu.Menu} this
27693          */
27694         beforeshow : true,
27695         /**
27696          * @event beforehide
27697          * Fires before this menu is hidden
27698          * @param {Roo.bootstrap.menu.Menu} this
27699          */
27700         beforehide : true,
27701         /**
27702          * @event show
27703          * Fires after this menu is displayed
27704          * @param {Roo.bootstrap.menu.Menu} this
27705          */
27706         show : true,
27707         /**
27708          * @event hide
27709          * Fires after this menu is hidden
27710          * @param {Roo.bootstrap.menu.Menu} this
27711          */
27712         hide : true,
27713         /**
27714          * @event click
27715          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27716          * @param {Roo.bootstrap.menu.Menu} this
27717          * @param {Roo.EventObject} e
27718          */
27719         click : true
27720     });
27721     
27722 };
27723
27724 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
27725     
27726     submenu : false,
27727     html : '',
27728     weight : 'default',
27729     icon : false,
27730     pos : 'bottom',
27731     
27732     
27733     getChildContainer : function() {
27734         if(this.isSubMenu){
27735             return this.el;
27736         }
27737         
27738         return this.el.select('ul.dropdown-menu', true).first();  
27739     },
27740     
27741     getAutoCreate : function()
27742     {
27743         var text = [
27744             {
27745                 tag : 'span',
27746                 cls : 'roo-menu-text',
27747                 html : this.html
27748             }
27749         ];
27750         
27751         if(this.icon){
27752             text.unshift({
27753                 tag : 'i',
27754                 cls : 'fa ' + this.icon
27755             })
27756         }
27757         
27758         
27759         var cfg = {
27760             tag : 'div',
27761             cls : 'btn-group',
27762             cn : [
27763                 {
27764                     tag : 'button',
27765                     cls : 'dropdown-button btn btn-' + this.weight,
27766                     cn : text
27767                 },
27768                 {
27769                     tag : 'button',
27770                     cls : 'dropdown-toggle btn btn-' + this.weight,
27771                     cn : [
27772                         {
27773                             tag : 'span',
27774                             cls : 'caret'
27775                         }
27776                     ]
27777                 },
27778                 {
27779                     tag : 'ul',
27780                     cls : 'dropdown-menu'
27781                 }
27782             ]
27783             
27784         };
27785         
27786         if(this.pos == 'top'){
27787             cfg.cls += ' dropup';
27788         }
27789         
27790         if(this.isSubMenu){
27791             cfg = {
27792                 tag : 'ul',
27793                 cls : 'dropdown-menu'
27794             }
27795         }
27796         
27797         return cfg;
27798     },
27799     
27800     onRender : function(ct, position)
27801     {
27802         this.isSubMenu = ct.hasClass('dropdown-submenu');
27803         
27804         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
27805     },
27806     
27807     initEvents : function() 
27808     {
27809         if(this.isSubMenu){
27810             return;
27811         }
27812         
27813         this.hidden = true;
27814         
27815         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
27816         this.triggerEl.on('click', this.onTriggerPress, this);
27817         
27818         this.buttonEl = this.el.select('button.dropdown-button', true).first();
27819         this.buttonEl.on('click', this.onClick, this);
27820         
27821     },
27822     
27823     list : function()
27824     {
27825         if(this.isSubMenu){
27826             return this.el;
27827         }
27828         
27829         return this.el.select('ul.dropdown-menu', true).first();
27830     },
27831     
27832     onClick : function(e)
27833     {
27834         this.fireEvent("click", this, e);
27835     },
27836     
27837     onTriggerPress  : function(e)
27838     {   
27839         if (this.isVisible()) {
27840             this.hide();
27841         } else {
27842             this.show();
27843         }
27844     },
27845     
27846     isVisible : function(){
27847         return !this.hidden;
27848     },
27849     
27850     show : function()
27851     {
27852         this.fireEvent("beforeshow", this);
27853         
27854         this.hidden = false;
27855         this.el.addClass('open');
27856         
27857         Roo.get(document).on("mouseup", this.onMouseUp, this);
27858         
27859         this.fireEvent("show", this);
27860         
27861         
27862     },
27863     
27864     hide : function()
27865     {
27866         this.fireEvent("beforehide", this);
27867         
27868         this.hidden = true;
27869         this.el.removeClass('open');
27870         
27871         Roo.get(document).un("mouseup", this.onMouseUp);
27872         
27873         this.fireEvent("hide", this);
27874     },
27875     
27876     onMouseUp : function()
27877     {
27878         this.hide();
27879     }
27880     
27881 });
27882
27883  
27884  /*
27885  * - LGPL
27886  *
27887  * menu item
27888  * 
27889  */
27890 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27891
27892 /**
27893  * @class Roo.bootstrap.menu.Item
27894  * @extends Roo.bootstrap.Component
27895  * Bootstrap MenuItem class
27896  * @cfg {Boolean} submenu (true | false) default false
27897  * @cfg {String} html text of the item
27898  * @cfg {String} href the link
27899  * @cfg {Boolean} disable (true | false) default false
27900  * @cfg {Boolean} preventDefault (true | false) default true
27901  * @cfg {String} icon Font awesome icon
27902  * @cfg {String} pos Submenu align to (left | right) default right 
27903  * 
27904  * 
27905  * @constructor
27906  * Create a new Item
27907  * @param {Object} config The config object
27908  */
27909
27910
27911 Roo.bootstrap.menu.Item = function(config){
27912     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
27913     this.addEvents({
27914         /**
27915          * @event mouseover
27916          * Fires when the mouse is hovering over this menu
27917          * @param {Roo.bootstrap.menu.Item} this
27918          * @param {Roo.EventObject} e
27919          */
27920         mouseover : true,
27921         /**
27922          * @event mouseout
27923          * Fires when the mouse exits this menu
27924          * @param {Roo.bootstrap.menu.Item} this
27925          * @param {Roo.EventObject} e
27926          */
27927         mouseout : true,
27928         // raw events
27929         /**
27930          * @event click
27931          * The raw click event for the entire grid.
27932          * @param {Roo.EventObject} e
27933          */
27934         click : true
27935     });
27936 };
27937
27938 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
27939     
27940     submenu : false,
27941     href : '',
27942     html : '',
27943     preventDefault: true,
27944     disable : false,
27945     icon : false,
27946     pos : 'right',
27947     
27948     getAutoCreate : function()
27949     {
27950         var text = [
27951             {
27952                 tag : 'span',
27953                 cls : 'roo-menu-item-text',
27954                 html : this.html
27955             }
27956         ];
27957         
27958         if(this.icon){
27959             text.unshift({
27960                 tag : 'i',
27961                 cls : 'fa ' + this.icon
27962             })
27963         }
27964         
27965         var cfg = {
27966             tag : 'li',
27967             cn : [
27968                 {
27969                     tag : 'a',
27970                     href : this.href || '#',
27971                     cn : text
27972                 }
27973             ]
27974         };
27975         
27976         if(this.disable){
27977             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
27978         }
27979         
27980         if(this.submenu){
27981             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
27982             
27983             if(this.pos == 'left'){
27984                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
27985             }
27986         }
27987         
27988         return cfg;
27989     },
27990     
27991     initEvents : function() 
27992     {
27993         this.el.on('mouseover', this.onMouseOver, this);
27994         this.el.on('mouseout', this.onMouseOut, this);
27995         
27996         this.el.select('a', true).first().on('click', this.onClick, this);
27997         
27998     },
27999     
28000     onClick : function(e)
28001     {
28002         if(this.preventDefault){
28003             e.preventDefault();
28004         }
28005         
28006         this.fireEvent("click", this, e);
28007     },
28008     
28009     onMouseOver : function(e)
28010     {
28011         if(this.submenu && this.pos == 'left'){
28012             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28013         }
28014         
28015         this.fireEvent("mouseover", this, e);
28016     },
28017     
28018     onMouseOut : function(e)
28019     {
28020         this.fireEvent("mouseout", this, e);
28021     }
28022 });
28023
28024  
28025
28026  /*
28027  * - LGPL
28028  *
28029  * menu separator
28030  * 
28031  */
28032 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28033
28034 /**
28035  * @class Roo.bootstrap.menu.Separator
28036  * @extends Roo.bootstrap.Component
28037  * Bootstrap Separator class
28038  * 
28039  * @constructor
28040  * Create a new Separator
28041  * @param {Object} config The config object
28042  */
28043
28044
28045 Roo.bootstrap.menu.Separator = function(config){
28046     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28047 };
28048
28049 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28050     
28051     getAutoCreate : function(){
28052         var cfg = {
28053             tag : 'li',
28054             cls: 'divider'
28055         };
28056         
28057         return cfg;
28058     }
28059    
28060 });
28061
28062  
28063
28064  /*
28065  * - LGPL
28066  *
28067  * Tooltip
28068  * 
28069  */
28070
28071 /**
28072  * @class Roo.bootstrap.Tooltip
28073  * Bootstrap Tooltip class
28074  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28075  * to determine which dom element triggers the tooltip.
28076  * 
28077  * It needs to add support for additional attributes like tooltip-position
28078  * 
28079  * @constructor
28080  * Create a new Toolti
28081  * @param {Object} config The config object
28082  */
28083
28084 Roo.bootstrap.Tooltip = function(config){
28085     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28086     
28087     this.alignment = Roo.bootstrap.Tooltip.alignment;
28088     
28089     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28090         this.alignment = config.alignment;
28091     }
28092     
28093 };
28094
28095 Roo.apply(Roo.bootstrap.Tooltip, {
28096     /**
28097      * @function init initialize tooltip monitoring.
28098      * @static
28099      */
28100     currentEl : false,
28101     currentTip : false,
28102     currentRegion : false,
28103     
28104     //  init : delay?
28105     
28106     init : function()
28107     {
28108         Roo.get(document).on('mouseover', this.enter ,this);
28109         Roo.get(document).on('mouseout', this.leave, this);
28110          
28111         
28112         this.currentTip = new Roo.bootstrap.Tooltip();
28113     },
28114     
28115     enter : function(ev)
28116     {
28117         var dom = ev.getTarget();
28118         
28119         //Roo.log(['enter',dom]);
28120         var el = Roo.fly(dom);
28121         if (this.currentEl) {
28122             //Roo.log(dom);
28123             //Roo.log(this.currentEl);
28124             //Roo.log(this.currentEl.contains(dom));
28125             if (this.currentEl == el) {
28126                 return;
28127             }
28128             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28129                 return;
28130             }
28131
28132         }
28133         
28134         if (this.currentTip.el) {
28135             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28136         }    
28137         //Roo.log(ev);
28138         
28139         if(!el || el.dom == document){
28140             return;
28141         }
28142         
28143         var bindEl = el;
28144         
28145         // you can not look for children, as if el is the body.. then everythign is the child..
28146         if (!el.attr('tooltip')) { //
28147             if (!el.select("[tooltip]").elements.length) {
28148                 return;
28149             }
28150             // is the mouse over this child...?
28151             bindEl = el.select("[tooltip]").first();
28152             var xy = ev.getXY();
28153             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28154                 //Roo.log("not in region.");
28155                 return;
28156             }
28157             //Roo.log("child element over..");
28158             
28159         }
28160         this.currentEl = bindEl;
28161         this.currentTip.bind(bindEl);
28162         this.currentRegion = Roo.lib.Region.getRegion(dom);
28163         this.currentTip.enter();
28164         
28165     },
28166     leave : function(ev)
28167     {
28168         var dom = ev.getTarget();
28169         //Roo.log(['leave',dom]);
28170         if (!this.currentEl) {
28171             return;
28172         }
28173         
28174         
28175         if (dom != this.currentEl.dom) {
28176             return;
28177         }
28178         var xy = ev.getXY();
28179         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28180             return;
28181         }
28182         // only activate leave if mouse cursor is outside... bounding box..
28183         
28184         
28185         
28186         
28187         if (this.currentTip) {
28188             this.currentTip.leave();
28189         }
28190         //Roo.log('clear currentEl');
28191         this.currentEl = false;
28192         
28193         
28194     },
28195     alignment : {
28196         'left' : ['r-l', [-2,0], 'right'],
28197         'right' : ['l-r', [2,0], 'left'],
28198         'bottom' : ['t-b', [0,2], 'top'],
28199         'top' : [ 'b-t', [0,-2], 'bottom']
28200     }
28201     
28202 });
28203
28204
28205 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28206     
28207     
28208     bindEl : false,
28209     
28210     delay : null, // can be { show : 300 , hide: 500}
28211     
28212     timeout : null,
28213     
28214     hoverState : null, //???
28215     
28216     placement : 'bottom', 
28217     
28218     alignment : false,
28219     
28220     getAutoCreate : function(){
28221     
28222         var cfg = {
28223            cls : 'tooltip',
28224            role : 'tooltip',
28225            cn : [
28226                 {
28227                     cls : 'tooltip-arrow'
28228                 },
28229                 {
28230                     cls : 'tooltip-inner'
28231                 }
28232            ]
28233         };
28234         
28235         return cfg;
28236     },
28237     bind : function(el)
28238     {
28239         this.bindEl = el;
28240     },
28241       
28242     
28243     enter : function () {
28244        
28245         if (this.timeout != null) {
28246             clearTimeout(this.timeout);
28247         }
28248         
28249         this.hoverState = 'in';
28250          //Roo.log("enter - show");
28251         if (!this.delay || !this.delay.show) {
28252             this.show();
28253             return;
28254         }
28255         var _t = this;
28256         this.timeout = setTimeout(function () {
28257             if (_t.hoverState == 'in') {
28258                 _t.show();
28259             }
28260         }, this.delay.show);
28261     },
28262     leave : function()
28263     {
28264         clearTimeout(this.timeout);
28265     
28266         this.hoverState = 'out';
28267          if (!this.delay || !this.delay.hide) {
28268             this.hide();
28269             return;
28270         }
28271        
28272         var _t = this;
28273         this.timeout = setTimeout(function () {
28274             //Roo.log("leave - timeout");
28275             
28276             if (_t.hoverState == 'out') {
28277                 _t.hide();
28278                 Roo.bootstrap.Tooltip.currentEl = false;
28279             }
28280         }, delay);
28281     },
28282     
28283     show : function (msg)
28284     {
28285         if (!this.el) {
28286             this.render(document.body);
28287         }
28288         // set content.
28289         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28290         
28291         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28292         
28293         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28294         
28295         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
28296         
28297         var placement = typeof this.placement == 'function' ?
28298             this.placement.call(this, this.el, on_el) :
28299             this.placement;
28300             
28301         var autoToken = /\s?auto?\s?/i;
28302         var autoPlace = autoToken.test(placement);
28303         if (autoPlace) {
28304             placement = placement.replace(autoToken, '') || 'top';
28305         }
28306         
28307         //this.el.detach()
28308         //this.el.setXY([0,0]);
28309         this.el.show();
28310         //this.el.dom.style.display='block';
28311         
28312         //this.el.appendTo(on_el);
28313         
28314         var p = this.getPosition();
28315         var box = this.el.getBox();
28316         
28317         if (autoPlace) {
28318             // fixme..
28319         }
28320         
28321         var align = this.alignment[placement];
28322         
28323         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28324         
28325         if(placement == 'top' || placement == 'bottom'){
28326             if(xy[0] < 0){
28327                 placement = 'right';
28328             }
28329             
28330             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28331                 placement = 'left';
28332             }
28333             
28334             var scroll = Roo.select('body', true).first().getScroll();
28335             
28336             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28337                 placement = 'top';
28338             }
28339             
28340             align = this.alignment[placement];
28341         }
28342         
28343         this.el.alignTo(this.bindEl, align[0],align[1]);
28344         //var arrow = this.el.select('.arrow',true).first();
28345         //arrow.set(align[2], 
28346         
28347         this.el.addClass(placement);
28348         
28349         this.el.addClass('in fade');
28350         
28351         this.hoverState = null;
28352         
28353         if (this.el.hasClass('fade')) {
28354             // fade it?
28355         }
28356         
28357     },
28358     hide : function()
28359     {
28360          
28361         if (!this.el) {
28362             return;
28363         }
28364         //this.el.setXY([0,0]);
28365         this.el.removeClass('in');
28366         //this.el.hide();
28367         
28368     }
28369     
28370 });
28371  
28372
28373  /*
28374  * - LGPL
28375  *
28376  * Location Picker
28377  * 
28378  */
28379
28380 /**
28381  * @class Roo.bootstrap.LocationPicker
28382  * @extends Roo.bootstrap.Component
28383  * Bootstrap LocationPicker class
28384  * @cfg {Number} latitude Position when init default 0
28385  * @cfg {Number} longitude Position when init default 0
28386  * @cfg {Number} zoom default 15
28387  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28388  * @cfg {Boolean} mapTypeControl default false
28389  * @cfg {Boolean} disableDoubleClickZoom default false
28390  * @cfg {Boolean} scrollwheel default true
28391  * @cfg {Boolean} streetViewControl default false
28392  * @cfg {Number} radius default 0
28393  * @cfg {String} locationName
28394  * @cfg {Boolean} draggable default true
28395  * @cfg {Boolean} enableAutocomplete default false
28396  * @cfg {Boolean} enableReverseGeocode default true
28397  * @cfg {String} markerTitle
28398  * 
28399  * @constructor
28400  * Create a new LocationPicker
28401  * @param {Object} config The config object
28402  */
28403
28404
28405 Roo.bootstrap.LocationPicker = function(config){
28406     
28407     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28408     
28409     this.addEvents({
28410         /**
28411          * @event initial
28412          * Fires when the picker initialized.
28413          * @param {Roo.bootstrap.LocationPicker} this
28414          * @param {Google Location} location
28415          */
28416         initial : true,
28417         /**
28418          * @event positionchanged
28419          * Fires when the picker position changed.
28420          * @param {Roo.bootstrap.LocationPicker} this
28421          * @param {Google Location} location
28422          */
28423         positionchanged : true,
28424         /**
28425          * @event resize
28426          * Fires when the map resize.
28427          * @param {Roo.bootstrap.LocationPicker} this
28428          */
28429         resize : true,
28430         /**
28431          * @event show
28432          * Fires when the map show.
28433          * @param {Roo.bootstrap.LocationPicker} this
28434          */
28435         show : true,
28436         /**
28437          * @event hide
28438          * Fires when the map hide.
28439          * @param {Roo.bootstrap.LocationPicker} this
28440          */
28441         hide : true,
28442         /**
28443          * @event mapClick
28444          * Fires when click the map.
28445          * @param {Roo.bootstrap.LocationPicker} this
28446          * @param {Map event} e
28447          */
28448         mapClick : true,
28449         /**
28450          * @event mapRightClick
28451          * Fires when right click the map.
28452          * @param {Roo.bootstrap.LocationPicker} this
28453          * @param {Map event} e
28454          */
28455         mapRightClick : true,
28456         /**
28457          * @event markerClick
28458          * Fires when click the marker.
28459          * @param {Roo.bootstrap.LocationPicker} this
28460          * @param {Map event} e
28461          */
28462         markerClick : true,
28463         /**
28464          * @event markerRightClick
28465          * Fires when right click the marker.
28466          * @param {Roo.bootstrap.LocationPicker} this
28467          * @param {Map event} e
28468          */
28469         markerRightClick : true,
28470         /**
28471          * @event OverlayViewDraw
28472          * Fires when OverlayView Draw
28473          * @param {Roo.bootstrap.LocationPicker} this
28474          */
28475         OverlayViewDraw : true,
28476         /**
28477          * @event OverlayViewOnAdd
28478          * Fires when OverlayView Draw
28479          * @param {Roo.bootstrap.LocationPicker} this
28480          */
28481         OverlayViewOnAdd : true,
28482         /**
28483          * @event OverlayViewOnRemove
28484          * Fires when OverlayView Draw
28485          * @param {Roo.bootstrap.LocationPicker} this
28486          */
28487         OverlayViewOnRemove : true,
28488         /**
28489          * @event OverlayViewShow
28490          * Fires when OverlayView Draw
28491          * @param {Roo.bootstrap.LocationPicker} this
28492          * @param {Pixel} cpx
28493          */
28494         OverlayViewShow : true,
28495         /**
28496          * @event OverlayViewHide
28497          * Fires when OverlayView Draw
28498          * @param {Roo.bootstrap.LocationPicker} this
28499          */
28500         OverlayViewHide : true,
28501         /**
28502          * @event loadexception
28503          * Fires when load google lib failed.
28504          * @param {Roo.bootstrap.LocationPicker} this
28505          */
28506         loadexception : true
28507     });
28508         
28509 };
28510
28511 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
28512     
28513     gMapContext: false,
28514     
28515     latitude: 0,
28516     longitude: 0,
28517     zoom: 15,
28518     mapTypeId: false,
28519     mapTypeControl: false,
28520     disableDoubleClickZoom: false,
28521     scrollwheel: true,
28522     streetViewControl: false,
28523     radius: 0,
28524     locationName: '',
28525     draggable: true,
28526     enableAutocomplete: false,
28527     enableReverseGeocode: true,
28528     markerTitle: '',
28529     
28530     getAutoCreate: function()
28531     {
28532
28533         var cfg = {
28534             tag: 'div',
28535             cls: 'roo-location-picker'
28536         };
28537         
28538         return cfg
28539     },
28540     
28541     initEvents: function(ct, position)
28542     {       
28543         if(!this.el.getWidth() || this.isApplied()){
28544             return;
28545         }
28546         
28547         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28548         
28549         this.initial();
28550     },
28551     
28552     initial: function()
28553     {
28554         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28555             this.fireEvent('loadexception', this);
28556             return;
28557         }
28558         
28559         if(!this.mapTypeId){
28560             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28561         }
28562         
28563         this.gMapContext = this.GMapContext();
28564         
28565         this.initOverlayView();
28566         
28567         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28568         
28569         var _this = this;
28570                 
28571         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28572             _this.setPosition(_this.gMapContext.marker.position);
28573         });
28574         
28575         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28576             _this.fireEvent('mapClick', this, event);
28577             
28578         });
28579
28580         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28581             _this.fireEvent('mapRightClick', this, event);
28582             
28583         });
28584         
28585         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28586             _this.fireEvent('markerClick', this, event);
28587             
28588         });
28589
28590         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28591             _this.fireEvent('markerRightClick', this, event);
28592             
28593         });
28594         
28595         this.setPosition(this.gMapContext.location);
28596         
28597         this.fireEvent('initial', this, this.gMapContext.location);
28598     },
28599     
28600     initOverlayView: function()
28601     {
28602         var _this = this;
28603         
28604         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28605             
28606             draw: function()
28607             {
28608                 _this.fireEvent('OverlayViewDraw', _this);
28609             },
28610             
28611             onAdd: function()
28612             {
28613                 _this.fireEvent('OverlayViewOnAdd', _this);
28614             },
28615             
28616             onRemove: function()
28617             {
28618                 _this.fireEvent('OverlayViewOnRemove', _this);
28619             },
28620             
28621             show: function(cpx)
28622             {
28623                 _this.fireEvent('OverlayViewShow', _this, cpx);
28624             },
28625             
28626             hide: function()
28627             {
28628                 _this.fireEvent('OverlayViewHide', _this);
28629             }
28630             
28631         });
28632     },
28633     
28634     fromLatLngToContainerPixel: function(event)
28635     {
28636         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28637     },
28638     
28639     isApplied: function() 
28640     {
28641         return this.getGmapContext() == false ? false : true;
28642     },
28643     
28644     getGmapContext: function() 
28645     {
28646         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28647     },
28648     
28649     GMapContext: function() 
28650     {
28651         var position = new google.maps.LatLng(this.latitude, this.longitude);
28652         
28653         var _map = new google.maps.Map(this.el.dom, {
28654             center: position,
28655             zoom: this.zoom,
28656             mapTypeId: this.mapTypeId,
28657             mapTypeControl: this.mapTypeControl,
28658             disableDoubleClickZoom: this.disableDoubleClickZoom,
28659             scrollwheel: this.scrollwheel,
28660             streetViewControl: this.streetViewControl,
28661             locationName: this.locationName,
28662             draggable: this.draggable,
28663             enableAutocomplete: this.enableAutocomplete,
28664             enableReverseGeocode: this.enableReverseGeocode
28665         });
28666         
28667         var _marker = new google.maps.Marker({
28668             position: position,
28669             map: _map,
28670             title: this.markerTitle,
28671             draggable: this.draggable
28672         });
28673         
28674         return {
28675             map: _map,
28676             marker: _marker,
28677             circle: null,
28678             location: position,
28679             radius: this.radius,
28680             locationName: this.locationName,
28681             addressComponents: {
28682                 formatted_address: null,
28683                 addressLine1: null,
28684                 addressLine2: null,
28685                 streetName: null,
28686                 streetNumber: null,
28687                 city: null,
28688                 district: null,
28689                 state: null,
28690                 stateOrProvince: null
28691             },
28692             settings: this,
28693             domContainer: this.el.dom,
28694             geodecoder: new google.maps.Geocoder()
28695         };
28696     },
28697     
28698     drawCircle: function(center, radius, options) 
28699     {
28700         if (this.gMapContext.circle != null) {
28701             this.gMapContext.circle.setMap(null);
28702         }
28703         if (radius > 0) {
28704             radius *= 1;
28705             options = Roo.apply({}, options, {
28706                 strokeColor: "#0000FF",
28707                 strokeOpacity: .35,
28708                 strokeWeight: 2,
28709                 fillColor: "#0000FF",
28710                 fillOpacity: .2
28711             });
28712             
28713             options.map = this.gMapContext.map;
28714             options.radius = radius;
28715             options.center = center;
28716             this.gMapContext.circle = new google.maps.Circle(options);
28717             return this.gMapContext.circle;
28718         }
28719         
28720         return null;
28721     },
28722     
28723     setPosition: function(location) 
28724     {
28725         this.gMapContext.location = location;
28726         this.gMapContext.marker.setPosition(location);
28727         this.gMapContext.map.panTo(location);
28728         this.drawCircle(location, this.gMapContext.radius, {});
28729         
28730         var _this = this;
28731         
28732         if (this.gMapContext.settings.enableReverseGeocode) {
28733             this.gMapContext.geodecoder.geocode({
28734                 latLng: this.gMapContext.location
28735             }, function(results, status) {
28736                 
28737                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28738                     _this.gMapContext.locationName = results[0].formatted_address;
28739                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28740                     
28741                     _this.fireEvent('positionchanged', this, location);
28742                 }
28743             });
28744             
28745             return;
28746         }
28747         
28748         this.fireEvent('positionchanged', this, location);
28749     },
28750     
28751     resize: function()
28752     {
28753         google.maps.event.trigger(this.gMapContext.map, "resize");
28754         
28755         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28756         
28757         this.fireEvent('resize', this);
28758     },
28759     
28760     setPositionByLatLng: function(latitude, longitude)
28761     {
28762         this.setPosition(new google.maps.LatLng(latitude, longitude));
28763     },
28764     
28765     getCurrentPosition: function() 
28766     {
28767         return {
28768             latitude: this.gMapContext.location.lat(),
28769             longitude: this.gMapContext.location.lng()
28770         };
28771     },
28772     
28773     getAddressName: function() 
28774     {
28775         return this.gMapContext.locationName;
28776     },
28777     
28778     getAddressComponents: function() 
28779     {
28780         return this.gMapContext.addressComponents;
28781     },
28782     
28783     address_component_from_google_geocode: function(address_components) 
28784     {
28785         var result = {};
28786         
28787         for (var i = 0; i < address_components.length; i++) {
28788             var component = address_components[i];
28789             if (component.types.indexOf("postal_code") >= 0) {
28790                 result.postalCode = component.short_name;
28791             } else if (component.types.indexOf("street_number") >= 0) {
28792                 result.streetNumber = component.short_name;
28793             } else if (component.types.indexOf("route") >= 0) {
28794                 result.streetName = component.short_name;
28795             } else if (component.types.indexOf("neighborhood") >= 0) {
28796                 result.city = component.short_name;
28797             } else if (component.types.indexOf("locality") >= 0) {
28798                 result.city = component.short_name;
28799             } else if (component.types.indexOf("sublocality") >= 0) {
28800                 result.district = component.short_name;
28801             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
28802                 result.stateOrProvince = component.short_name;
28803             } else if (component.types.indexOf("country") >= 0) {
28804                 result.country = component.short_name;
28805             }
28806         }
28807         
28808         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
28809         result.addressLine2 = "";
28810         return result;
28811     },
28812     
28813     setZoomLevel: function(zoom)
28814     {
28815         this.gMapContext.map.setZoom(zoom);
28816     },
28817     
28818     show: function()
28819     {
28820         if(!this.el){
28821             return;
28822         }
28823         
28824         this.el.show();
28825         
28826         this.resize();
28827         
28828         this.fireEvent('show', this);
28829     },
28830     
28831     hide: function()
28832     {
28833         if(!this.el){
28834             return;
28835         }
28836         
28837         this.el.hide();
28838         
28839         this.fireEvent('hide', this);
28840     }
28841     
28842 });
28843
28844 Roo.apply(Roo.bootstrap.LocationPicker, {
28845     
28846     OverlayView : function(map, options)
28847     {
28848         options = options || {};
28849         
28850         this.setMap(map);
28851     }
28852     
28853     
28854 });/**
28855  * @class Roo.bootstrap.Alert
28856  * @extends Roo.bootstrap.Component
28857  * Bootstrap Alert class - shows an alert area box
28858  * eg
28859  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
28860   Enter a valid email address
28861 </div>
28862  * @licence LGPL
28863  * @cfg {String} title The title of alert
28864  * @cfg {String} html The content of alert
28865  * @cfg {String} weight (  success | info | warning | danger )
28866  * @cfg {String} faicon font-awesomeicon
28867  * 
28868  * @constructor
28869  * Create a new alert
28870  * @param {Object} config The config object
28871  */
28872
28873
28874 Roo.bootstrap.Alert = function(config){
28875     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
28876     
28877 };
28878
28879 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
28880     
28881     title: '',
28882     html: '',
28883     weight: false,
28884     faicon: false,
28885     
28886     getAutoCreate : function()
28887     {
28888         
28889         var cfg = {
28890             tag : 'div',
28891             cls : 'alert',
28892             cn : [
28893                 {
28894                     tag : 'i',
28895                     cls : 'roo-alert-icon'
28896                     
28897                 },
28898                 {
28899                     tag : 'b',
28900                     cls : 'roo-alert-title',
28901                     html : this.title
28902                 },
28903                 {
28904                     tag : 'span',
28905                     cls : 'roo-alert-text',
28906                     html : this.html
28907                 }
28908             ]
28909         };
28910         
28911         if(this.faicon){
28912             cfg.cn[0].cls += ' fa ' + this.faicon;
28913         }
28914         
28915         if(this.weight){
28916             cfg.cls += ' alert-' + this.weight;
28917         }
28918         
28919         return cfg;
28920     },
28921     
28922     initEvents: function() 
28923     {
28924         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28925     },
28926     
28927     setTitle : function(str)
28928     {
28929         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
28930     },
28931     
28932     setText : function(str)
28933     {
28934         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
28935     },
28936     
28937     setWeight : function(weight)
28938     {
28939         if(this.weight){
28940             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
28941         }
28942         
28943         this.weight = weight;
28944         
28945         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
28946     },
28947     
28948     setIcon : function(icon)
28949     {
28950         if(this.faicon){
28951             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
28952         }
28953         
28954         this.faicon = icon;
28955         
28956         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
28957     },
28958     
28959     hide: function() 
28960     {
28961         this.el.hide();   
28962     },
28963     
28964     show: function() 
28965     {  
28966         this.el.show();   
28967     }
28968     
28969 });
28970
28971  
28972 /*
28973 * Licence: LGPL
28974 */
28975
28976 /**
28977  * @class Roo.bootstrap.UploadCropbox
28978  * @extends Roo.bootstrap.Component
28979  * Bootstrap UploadCropbox class
28980  * @cfg {String} emptyText show when image has been loaded
28981  * @cfg {String} rotateNotify show when image too small to rotate
28982  * @cfg {Number} errorTimeout default 3000
28983  * @cfg {Number} minWidth default 300
28984  * @cfg {Number} minHeight default 300
28985  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
28986  * @cfg {Boolean} isDocument (true|false) default false
28987  * @cfg {String} url action url
28988  * @cfg {String} paramName default 'imageUpload'
28989  * @cfg {String} method default POST
28990  * @cfg {Boolean} loadMask (true|false) default true
28991  * @cfg {Boolean} loadingText default 'Loading...'
28992  * 
28993  * @constructor
28994  * Create a new UploadCropbox
28995  * @param {Object} config The config object
28996  */
28997
28998 Roo.bootstrap.UploadCropbox = function(config){
28999     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29000     
29001     this.addEvents({
29002         /**
29003          * @event beforeselectfile
29004          * Fire before select file
29005          * @param {Roo.bootstrap.UploadCropbox} this
29006          */
29007         "beforeselectfile" : true,
29008         /**
29009          * @event initial
29010          * Fire after initEvent
29011          * @param {Roo.bootstrap.UploadCropbox} this
29012          */
29013         "initial" : true,
29014         /**
29015          * @event crop
29016          * Fire after initEvent
29017          * @param {Roo.bootstrap.UploadCropbox} this
29018          * @param {String} data
29019          */
29020         "crop" : true,
29021         /**
29022          * @event prepare
29023          * Fire when preparing the file data
29024          * @param {Roo.bootstrap.UploadCropbox} this
29025          * @param {Object} file
29026          */
29027         "prepare" : true,
29028         /**
29029          * @event exception
29030          * Fire when get exception
29031          * @param {Roo.bootstrap.UploadCropbox} this
29032          * @param {XMLHttpRequest} xhr
29033          */
29034         "exception" : true,
29035         /**
29036          * @event beforeloadcanvas
29037          * Fire before load the canvas
29038          * @param {Roo.bootstrap.UploadCropbox} this
29039          * @param {String} src
29040          */
29041         "beforeloadcanvas" : true,
29042         /**
29043          * @event trash
29044          * Fire when trash image
29045          * @param {Roo.bootstrap.UploadCropbox} this
29046          */
29047         "trash" : true,
29048         /**
29049          * @event download
29050          * Fire when download the image
29051          * @param {Roo.bootstrap.UploadCropbox} this
29052          */
29053         "download" : true,
29054         /**
29055          * @event footerbuttonclick
29056          * Fire when footerbuttonclick
29057          * @param {Roo.bootstrap.UploadCropbox} this
29058          * @param {String} type
29059          */
29060         "footerbuttonclick" : true,
29061         /**
29062          * @event resize
29063          * Fire when resize
29064          * @param {Roo.bootstrap.UploadCropbox} this
29065          */
29066         "resize" : true,
29067         /**
29068          * @event rotate
29069          * Fire when rotate the image
29070          * @param {Roo.bootstrap.UploadCropbox} this
29071          * @param {String} pos
29072          */
29073         "rotate" : true,
29074         /**
29075          * @event inspect
29076          * Fire when inspect the file
29077          * @param {Roo.bootstrap.UploadCropbox} this
29078          * @param {Object} file
29079          */
29080         "inspect" : true,
29081         /**
29082          * @event upload
29083          * Fire when xhr upload the file
29084          * @param {Roo.bootstrap.UploadCropbox} this
29085          * @param {Object} data
29086          */
29087         "upload" : true,
29088         /**
29089          * @event arrange
29090          * Fire when arrange the file data
29091          * @param {Roo.bootstrap.UploadCropbox} this
29092          * @param {Object} formData
29093          */
29094         "arrange" : true
29095     });
29096     
29097     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29098 };
29099
29100 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29101     
29102     emptyText : 'Click to upload image',
29103     rotateNotify : 'Image is too small to rotate',
29104     errorTimeout : 3000,
29105     scale : 0,
29106     baseScale : 1,
29107     rotate : 0,
29108     dragable : false,
29109     pinching : false,
29110     mouseX : 0,
29111     mouseY : 0,
29112     cropData : false,
29113     minWidth : 300,
29114     minHeight : 300,
29115     file : false,
29116     exif : {},
29117     baseRotate : 1,
29118     cropType : 'image/jpeg',
29119     buttons : false,
29120     canvasLoaded : false,
29121     isDocument : false,
29122     method : 'POST',
29123     paramName : 'imageUpload',
29124     loadMask : true,
29125     loadingText : 'Loading...',
29126     maskEl : false,
29127     
29128     getAutoCreate : function()
29129     {
29130         var cfg = {
29131             tag : 'div',
29132             cls : 'roo-upload-cropbox',
29133             cn : [
29134                 {
29135                     tag : 'input',
29136                     cls : 'roo-upload-cropbox-selector',
29137                     type : 'file'
29138                 },
29139                 {
29140                     tag : 'div',
29141                     cls : 'roo-upload-cropbox-body',
29142                     style : 'cursor:pointer',
29143                     cn : [
29144                         {
29145                             tag : 'div',
29146                             cls : 'roo-upload-cropbox-preview'
29147                         },
29148                         {
29149                             tag : 'div',
29150                             cls : 'roo-upload-cropbox-thumb'
29151                         },
29152                         {
29153                             tag : 'div',
29154                             cls : 'roo-upload-cropbox-empty-notify',
29155                             html : this.emptyText
29156                         },
29157                         {
29158                             tag : 'div',
29159                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29160                             html : this.rotateNotify
29161                         }
29162                     ]
29163                 },
29164                 {
29165                     tag : 'div',
29166                     cls : 'roo-upload-cropbox-footer',
29167                     cn : {
29168                         tag : 'div',
29169                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29170                         cn : []
29171                     }
29172                 }
29173             ]
29174         };
29175         
29176         return cfg;
29177     },
29178     
29179     onRender : function(ct, position)
29180     {
29181         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29182         
29183         if (this.buttons.length) {
29184             
29185             Roo.each(this.buttons, function(bb) {
29186                 
29187                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29188                 
29189                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29190                 
29191             }, this);
29192         }
29193         
29194         if(this.loadMask){
29195             this.maskEl = this.el;
29196         }
29197     },
29198     
29199     initEvents : function()
29200     {
29201         this.urlAPI = (window.createObjectURL && window) || 
29202                                 (window.URL && URL.revokeObjectURL && URL) || 
29203                                 (window.webkitURL && webkitURL);
29204                         
29205         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29206         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29207         
29208         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29209         this.selectorEl.hide();
29210         
29211         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29212         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29213         
29214         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29215         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29216         this.thumbEl.hide();
29217         
29218         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29219         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29220         
29221         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29222         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29223         this.errorEl.hide();
29224         
29225         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29226         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29227         this.footerEl.hide();
29228         
29229         this.setThumbBoxSize();
29230         
29231         this.bind();
29232         
29233         this.resize();
29234         
29235         this.fireEvent('initial', this);
29236     },
29237
29238     bind : function()
29239     {
29240         var _this = this;
29241         
29242         window.addEventListener("resize", function() { _this.resize(); } );
29243         
29244         this.bodyEl.on('click', this.beforeSelectFile, this);
29245         
29246         if(Roo.isTouch){
29247             this.bodyEl.on('touchstart', this.onTouchStart, this);
29248             this.bodyEl.on('touchmove', this.onTouchMove, this);
29249             this.bodyEl.on('touchend', this.onTouchEnd, this);
29250         }
29251         
29252         if(!Roo.isTouch){
29253             this.bodyEl.on('mousedown', this.onMouseDown, this);
29254             this.bodyEl.on('mousemove', this.onMouseMove, this);
29255             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29256             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29257             Roo.get(document).on('mouseup', this.onMouseUp, this);
29258         }
29259         
29260         this.selectorEl.on('change', this.onFileSelected, this);
29261     },
29262     
29263     reset : function()
29264     {    
29265         this.scale = 0;
29266         this.baseScale = 1;
29267         this.rotate = 0;
29268         this.baseRotate = 1;
29269         this.dragable = false;
29270         this.pinching = false;
29271         this.mouseX = 0;
29272         this.mouseY = 0;
29273         this.cropData = false;
29274         this.notifyEl.dom.innerHTML = this.emptyText;
29275         
29276         this.selectorEl.dom.value = '';
29277         
29278     },
29279     
29280     resize : function()
29281     {
29282         if(this.fireEvent('resize', this) != false){
29283             this.setThumbBoxPosition();
29284             this.setCanvasPosition();
29285         }
29286     },
29287     
29288     onFooterButtonClick : function(e, el, o, type)
29289     {
29290         switch (type) {
29291             case 'rotate-left' :
29292                 this.onRotateLeft(e);
29293                 break;
29294             case 'rotate-right' :
29295                 this.onRotateRight(e);
29296                 break;
29297             case 'picture' :
29298                 this.beforeSelectFile(e);
29299                 break;
29300             case 'trash' :
29301                 this.trash(e);
29302                 break;
29303             case 'crop' :
29304                 this.crop(e);
29305                 break;
29306             case 'download' :
29307                 this.download(e);
29308                 break;
29309             default :
29310                 break;
29311         }
29312         
29313         this.fireEvent('footerbuttonclick', this, type);
29314     },
29315     
29316     beforeSelectFile : function(e)
29317     {
29318         e.preventDefault();
29319         
29320         if(this.fireEvent('beforeselectfile', this) != false){
29321             this.selectorEl.dom.click();
29322         }
29323     },
29324     
29325     onFileSelected : function(e)
29326     {
29327         e.preventDefault();
29328         
29329         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29330             return;
29331         }
29332         
29333         var file = this.selectorEl.dom.files[0];
29334         
29335         if(this.fireEvent('inspect', this, file) != false){
29336             this.prepare(file);
29337         }
29338         
29339     },
29340     
29341     trash : function(e)
29342     {
29343         this.fireEvent('trash', this);
29344     },
29345     
29346     download : function(e)
29347     {
29348         this.fireEvent('download', this);
29349     },
29350     
29351     loadCanvas : function(src)
29352     {   
29353         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29354             
29355             this.reset();
29356             
29357             this.imageEl = document.createElement('img');
29358             
29359             var _this = this;
29360             
29361             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29362             
29363             this.imageEl.src = src;
29364         }
29365     },
29366     
29367     onLoadCanvas : function()
29368     {   
29369         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29370         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29371         
29372         this.bodyEl.un('click', this.beforeSelectFile, this);
29373         
29374         this.notifyEl.hide();
29375         this.thumbEl.show();
29376         this.footerEl.show();
29377         
29378         this.baseRotateLevel();
29379         
29380         if(this.isDocument){
29381             this.setThumbBoxSize();
29382         }
29383         
29384         this.setThumbBoxPosition();
29385         
29386         this.baseScaleLevel();
29387         
29388         this.draw();
29389         
29390         this.resize();
29391         
29392         this.canvasLoaded = true;
29393         
29394         if(this.loadMask){
29395             this.maskEl.unmask();
29396         }
29397         
29398     },
29399     
29400     setCanvasPosition : function()
29401     {   
29402         if(!this.canvasEl){
29403             return;
29404         }
29405         
29406         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29407         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29408         
29409         this.previewEl.setLeft(pw);
29410         this.previewEl.setTop(ph);
29411         
29412     },
29413     
29414     onMouseDown : function(e)
29415     {   
29416         e.stopEvent();
29417         
29418         this.dragable = true;
29419         this.pinching = false;
29420         
29421         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29422             this.dragable = false;
29423             return;
29424         }
29425         
29426         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29427         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29428         
29429     },
29430     
29431     onMouseMove : function(e)
29432     {   
29433         e.stopEvent();
29434         
29435         if(!this.canvasLoaded){
29436             return;
29437         }
29438         
29439         if (!this.dragable){
29440             return;
29441         }
29442         
29443         var minX = Math.ceil(this.thumbEl.getLeft(true));
29444         var minY = Math.ceil(this.thumbEl.getTop(true));
29445         
29446         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29447         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29448         
29449         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29450         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29451         
29452         x = x - this.mouseX;
29453         y = y - this.mouseY;
29454         
29455         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29456         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29457         
29458         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29459         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29460         
29461         this.previewEl.setLeft(bgX);
29462         this.previewEl.setTop(bgY);
29463         
29464         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29465         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29466     },
29467     
29468     onMouseUp : function(e)
29469     {   
29470         e.stopEvent();
29471         
29472         this.dragable = false;
29473     },
29474     
29475     onMouseWheel : function(e)
29476     {   
29477         e.stopEvent();
29478         
29479         this.startScale = this.scale;
29480         
29481         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29482         
29483         if(!this.zoomable()){
29484             this.scale = this.startScale;
29485             return;
29486         }
29487         
29488         this.draw();
29489         
29490         return;
29491     },
29492     
29493     zoomable : function()
29494     {
29495         var minScale = this.thumbEl.getWidth() / this.minWidth;
29496         
29497         if(this.minWidth < this.minHeight){
29498             minScale = this.thumbEl.getHeight() / this.minHeight;
29499         }
29500         
29501         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29502         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29503         
29504         if(
29505                 this.isDocument &&
29506                 (this.rotate == 0 || this.rotate == 180) && 
29507                 (
29508                     width > this.imageEl.OriginWidth || 
29509                     height > this.imageEl.OriginHeight ||
29510                     (width < this.minWidth && height < this.minHeight)
29511                 )
29512         ){
29513             return false;
29514         }
29515         
29516         if(
29517                 this.isDocument &&
29518                 (this.rotate == 90 || this.rotate == 270) && 
29519                 (
29520                     width > this.imageEl.OriginWidth || 
29521                     height > this.imageEl.OriginHeight ||
29522                     (width < this.minHeight && height < this.minWidth)
29523                 )
29524         ){
29525             return false;
29526         }
29527         
29528         if(
29529                 !this.isDocument &&
29530                 (this.rotate == 0 || this.rotate == 180) && 
29531                 (
29532                     width < this.minWidth || 
29533                     width > this.imageEl.OriginWidth || 
29534                     height < this.minHeight || 
29535                     height > this.imageEl.OriginHeight
29536                 )
29537         ){
29538             return false;
29539         }
29540         
29541         if(
29542                 !this.isDocument &&
29543                 (this.rotate == 90 || this.rotate == 270) && 
29544                 (
29545                     width < this.minHeight || 
29546                     width > this.imageEl.OriginWidth || 
29547                     height < this.minWidth || 
29548                     height > this.imageEl.OriginHeight
29549                 )
29550         ){
29551             return false;
29552         }
29553         
29554         return true;
29555         
29556     },
29557     
29558     onRotateLeft : function(e)
29559     {   
29560         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29561             
29562             var minScale = this.thumbEl.getWidth() / this.minWidth;
29563             
29564             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29565             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29566             
29567             this.startScale = this.scale;
29568             
29569             while (this.getScaleLevel() < minScale){
29570             
29571                 this.scale = this.scale + 1;
29572                 
29573                 if(!this.zoomable()){
29574                     break;
29575                 }
29576                 
29577                 if(
29578                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29579                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29580                 ){
29581                     continue;
29582                 }
29583                 
29584                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29585
29586                 this.draw();
29587                 
29588                 return;
29589             }
29590             
29591             this.scale = this.startScale;
29592             
29593             this.onRotateFail();
29594             
29595             return false;
29596         }
29597         
29598         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29599
29600         if(this.isDocument){
29601             this.setThumbBoxSize();
29602             this.setThumbBoxPosition();
29603             this.setCanvasPosition();
29604         }
29605         
29606         this.draw();
29607         
29608         this.fireEvent('rotate', this, 'left');
29609         
29610     },
29611     
29612     onRotateRight : function(e)
29613     {
29614         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29615             
29616             var minScale = this.thumbEl.getWidth() / this.minWidth;
29617         
29618             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29619             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29620             
29621             this.startScale = this.scale;
29622             
29623             while (this.getScaleLevel() < minScale){
29624             
29625                 this.scale = this.scale + 1;
29626                 
29627                 if(!this.zoomable()){
29628                     break;
29629                 }
29630                 
29631                 if(
29632                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29633                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29634                 ){
29635                     continue;
29636                 }
29637                 
29638                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29639
29640                 this.draw();
29641                 
29642                 return;
29643             }
29644             
29645             this.scale = this.startScale;
29646             
29647             this.onRotateFail();
29648             
29649             return false;
29650         }
29651         
29652         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29653
29654         if(this.isDocument){
29655             this.setThumbBoxSize();
29656             this.setThumbBoxPosition();
29657             this.setCanvasPosition();
29658         }
29659         
29660         this.draw();
29661         
29662         this.fireEvent('rotate', this, 'right');
29663     },
29664     
29665     onRotateFail : function()
29666     {
29667         this.errorEl.show(true);
29668         
29669         var _this = this;
29670         
29671         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29672     },
29673     
29674     draw : function()
29675     {
29676         this.previewEl.dom.innerHTML = '';
29677         
29678         var canvasEl = document.createElement("canvas");
29679         
29680         var contextEl = canvasEl.getContext("2d");
29681         
29682         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29683         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29684         var center = this.imageEl.OriginWidth / 2;
29685         
29686         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29687             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29688             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29689             center = this.imageEl.OriginHeight / 2;
29690         }
29691         
29692         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29693         
29694         contextEl.translate(center, center);
29695         contextEl.rotate(this.rotate * Math.PI / 180);
29696
29697         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29698         
29699         this.canvasEl = document.createElement("canvas");
29700         
29701         this.contextEl = this.canvasEl.getContext("2d");
29702         
29703         switch (this.rotate) {
29704             case 0 :
29705                 
29706                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29707                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29708                 
29709                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29710                 
29711                 break;
29712             case 90 : 
29713                 
29714                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29715                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29716                 
29717                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29718                     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);
29719                     break;
29720                 }
29721                 
29722                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29723                 
29724                 break;
29725             case 180 :
29726                 
29727                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29728                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29729                 
29730                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29731                     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);
29732                     break;
29733                 }
29734                 
29735                 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);
29736                 
29737                 break;
29738             case 270 :
29739                 
29740                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29741                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29742         
29743                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29744                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29745                     break;
29746                 }
29747                 
29748                 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);
29749                 
29750                 break;
29751             default : 
29752                 break;
29753         }
29754         
29755         this.previewEl.appendChild(this.canvasEl);
29756         
29757         this.setCanvasPosition();
29758     },
29759     
29760     crop : function()
29761     {
29762         if(!this.canvasLoaded){
29763             return;
29764         }
29765         
29766         var imageCanvas = document.createElement("canvas");
29767         
29768         var imageContext = imageCanvas.getContext("2d");
29769         
29770         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29771         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29772         
29773         var center = imageCanvas.width / 2;
29774         
29775         imageContext.translate(center, center);
29776         
29777         imageContext.rotate(this.rotate * Math.PI / 180);
29778         
29779         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29780         
29781         var canvas = document.createElement("canvas");
29782         
29783         var context = canvas.getContext("2d");
29784                 
29785         canvas.width = this.minWidth;
29786         canvas.height = this.minHeight;
29787
29788         switch (this.rotate) {
29789             case 0 :
29790                 
29791                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29792                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29793                 
29794                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29795                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29796                 
29797                 var targetWidth = this.minWidth - 2 * x;
29798                 var targetHeight = this.minHeight - 2 * y;
29799                 
29800                 var scale = 1;
29801                 
29802                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29803                     scale = targetWidth / width;
29804                 }
29805                 
29806                 if(x > 0 && y == 0){
29807                     scale = targetHeight / height;
29808                 }
29809                 
29810                 if(x > 0 && y > 0){
29811                     scale = targetWidth / width;
29812                     
29813                     if(width < height){
29814                         scale = targetHeight / height;
29815                     }
29816                 }
29817                 
29818                 context.scale(scale, scale);
29819                 
29820                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29821                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29822
29823                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29824                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29825
29826                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29827                 
29828                 break;
29829             case 90 : 
29830                 
29831                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29832                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29833                 
29834                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29835                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29836                 
29837                 var targetWidth = this.minWidth - 2 * x;
29838                 var targetHeight = this.minHeight - 2 * y;
29839                 
29840                 var scale = 1;
29841                 
29842                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29843                     scale = targetWidth / width;
29844                 }
29845                 
29846                 if(x > 0 && y == 0){
29847                     scale = targetHeight / height;
29848                 }
29849                 
29850                 if(x > 0 && y > 0){
29851                     scale = targetWidth / width;
29852                     
29853                     if(width < height){
29854                         scale = targetHeight / height;
29855                     }
29856                 }
29857                 
29858                 context.scale(scale, scale);
29859                 
29860                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29861                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29862
29863                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29864                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29865                 
29866                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29867                 
29868                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29869                 
29870                 break;
29871             case 180 :
29872                 
29873                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29874                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29875                 
29876                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29877                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29878                 
29879                 var targetWidth = this.minWidth - 2 * x;
29880                 var targetHeight = this.minHeight - 2 * y;
29881                 
29882                 var scale = 1;
29883                 
29884                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29885                     scale = targetWidth / width;
29886                 }
29887                 
29888                 if(x > 0 && y == 0){
29889                     scale = targetHeight / height;
29890                 }
29891                 
29892                 if(x > 0 && y > 0){
29893                     scale = targetWidth / width;
29894                     
29895                     if(width < height){
29896                         scale = targetHeight / height;
29897                     }
29898                 }
29899                 
29900                 context.scale(scale, scale);
29901                 
29902                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29903                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29904
29905                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29906                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29907
29908                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29909                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29910                 
29911                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29912                 
29913                 break;
29914             case 270 :
29915                 
29916                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29917                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29918                 
29919                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29920                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29921                 
29922                 var targetWidth = this.minWidth - 2 * x;
29923                 var targetHeight = this.minHeight - 2 * y;
29924                 
29925                 var scale = 1;
29926                 
29927                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29928                     scale = targetWidth / width;
29929                 }
29930                 
29931                 if(x > 0 && y == 0){
29932                     scale = targetHeight / height;
29933                 }
29934                 
29935                 if(x > 0 && y > 0){
29936                     scale = targetWidth / width;
29937                     
29938                     if(width < height){
29939                         scale = targetHeight / height;
29940                     }
29941                 }
29942                 
29943                 context.scale(scale, scale);
29944                 
29945                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29946                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29947
29948                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29949                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29950                 
29951                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29952                 
29953                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29954                 
29955                 break;
29956             default : 
29957                 break;
29958         }
29959         
29960         this.cropData = canvas.toDataURL(this.cropType);
29961         
29962         if(this.fireEvent('crop', this, this.cropData) !== false){
29963             this.process(this.file, this.cropData);
29964         }
29965         
29966         return;
29967         
29968     },
29969     
29970     setThumbBoxSize : function()
29971     {
29972         var width, height;
29973         
29974         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
29975             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
29976             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
29977             
29978             this.minWidth = width;
29979             this.minHeight = height;
29980             
29981             if(this.rotate == 90 || this.rotate == 270){
29982                 this.minWidth = height;
29983                 this.minHeight = width;
29984             }
29985         }
29986         
29987         height = 300;
29988         width = Math.ceil(this.minWidth * height / this.minHeight);
29989         
29990         if(this.minWidth > this.minHeight){
29991             width = 300;
29992             height = Math.ceil(this.minHeight * width / this.minWidth);
29993         }
29994         
29995         this.thumbEl.setStyle({
29996             width : width + 'px',
29997             height : height + 'px'
29998         });
29999
30000         return;
30001             
30002     },
30003     
30004     setThumbBoxPosition : function()
30005     {
30006         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30007         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30008         
30009         this.thumbEl.setLeft(x);
30010         this.thumbEl.setTop(y);
30011         
30012     },
30013     
30014     baseRotateLevel : function()
30015     {
30016         this.baseRotate = 1;
30017         
30018         if(
30019                 typeof(this.exif) != 'undefined' &&
30020                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30021                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30022         ){
30023             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30024         }
30025         
30026         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30027         
30028     },
30029     
30030     baseScaleLevel : function()
30031     {
30032         var width, height;
30033         
30034         if(this.isDocument){
30035             
30036             if(this.baseRotate == 6 || this.baseRotate == 8){
30037             
30038                 height = this.thumbEl.getHeight();
30039                 this.baseScale = height / this.imageEl.OriginWidth;
30040
30041                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30042                     width = this.thumbEl.getWidth();
30043                     this.baseScale = width / this.imageEl.OriginHeight;
30044                 }
30045
30046                 return;
30047             }
30048
30049             height = this.thumbEl.getHeight();
30050             this.baseScale = height / this.imageEl.OriginHeight;
30051
30052             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30053                 width = this.thumbEl.getWidth();
30054                 this.baseScale = width / this.imageEl.OriginWidth;
30055             }
30056
30057             return;
30058         }
30059         
30060         if(this.baseRotate == 6 || this.baseRotate == 8){
30061             
30062             width = this.thumbEl.getHeight();
30063             this.baseScale = width / this.imageEl.OriginHeight;
30064             
30065             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30066                 height = this.thumbEl.getWidth();
30067                 this.baseScale = height / this.imageEl.OriginHeight;
30068             }
30069             
30070             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30071                 height = this.thumbEl.getWidth();
30072                 this.baseScale = height / this.imageEl.OriginHeight;
30073                 
30074                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30075                     width = this.thumbEl.getHeight();
30076                     this.baseScale = width / this.imageEl.OriginWidth;
30077                 }
30078             }
30079             
30080             return;
30081         }
30082         
30083         width = this.thumbEl.getWidth();
30084         this.baseScale = width / this.imageEl.OriginWidth;
30085         
30086         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30087             height = this.thumbEl.getHeight();
30088             this.baseScale = height / this.imageEl.OriginHeight;
30089         }
30090         
30091         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30092             
30093             height = this.thumbEl.getHeight();
30094             this.baseScale = height / this.imageEl.OriginHeight;
30095             
30096             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30097                 width = this.thumbEl.getWidth();
30098                 this.baseScale = width / this.imageEl.OriginWidth;
30099             }
30100             
30101         }
30102         
30103         return;
30104     },
30105     
30106     getScaleLevel : function()
30107     {
30108         return this.baseScale * Math.pow(1.1, this.scale);
30109     },
30110     
30111     onTouchStart : function(e)
30112     {
30113         if(!this.canvasLoaded){
30114             this.beforeSelectFile(e);
30115             return;
30116         }
30117         
30118         var touches = e.browserEvent.touches;
30119         
30120         if(!touches){
30121             return;
30122         }
30123         
30124         if(touches.length == 1){
30125             this.onMouseDown(e);
30126             return;
30127         }
30128         
30129         if(touches.length != 2){
30130             return;
30131         }
30132         
30133         var coords = [];
30134         
30135         for(var i = 0, finger; finger = touches[i]; i++){
30136             coords.push(finger.pageX, finger.pageY);
30137         }
30138         
30139         var x = Math.pow(coords[0] - coords[2], 2);
30140         var y = Math.pow(coords[1] - coords[3], 2);
30141         
30142         this.startDistance = Math.sqrt(x + y);
30143         
30144         this.startScale = this.scale;
30145         
30146         this.pinching = true;
30147         this.dragable = false;
30148         
30149     },
30150     
30151     onTouchMove : function(e)
30152     {
30153         if(!this.pinching && !this.dragable){
30154             return;
30155         }
30156         
30157         var touches = e.browserEvent.touches;
30158         
30159         if(!touches){
30160             return;
30161         }
30162         
30163         if(this.dragable){
30164             this.onMouseMove(e);
30165             return;
30166         }
30167         
30168         var coords = [];
30169         
30170         for(var i = 0, finger; finger = touches[i]; i++){
30171             coords.push(finger.pageX, finger.pageY);
30172         }
30173         
30174         var x = Math.pow(coords[0] - coords[2], 2);
30175         var y = Math.pow(coords[1] - coords[3], 2);
30176         
30177         this.endDistance = Math.sqrt(x + y);
30178         
30179         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30180         
30181         if(!this.zoomable()){
30182             this.scale = this.startScale;
30183             return;
30184         }
30185         
30186         this.draw();
30187         
30188     },
30189     
30190     onTouchEnd : function(e)
30191     {
30192         this.pinching = false;
30193         this.dragable = false;
30194         
30195     },
30196     
30197     process : function(file, crop)
30198     {
30199         if(this.loadMask){
30200             this.maskEl.mask(this.loadingText);
30201         }
30202         
30203         this.xhr = new XMLHttpRequest();
30204         
30205         file.xhr = this.xhr;
30206
30207         this.xhr.open(this.method, this.url, true);
30208         
30209         var headers = {
30210             "Accept": "application/json",
30211             "Cache-Control": "no-cache",
30212             "X-Requested-With": "XMLHttpRequest"
30213         };
30214         
30215         for (var headerName in headers) {
30216             var headerValue = headers[headerName];
30217             if (headerValue) {
30218                 this.xhr.setRequestHeader(headerName, headerValue);
30219             }
30220         }
30221         
30222         var _this = this;
30223         
30224         this.xhr.onload = function()
30225         {
30226             _this.xhrOnLoad(_this.xhr);
30227         }
30228         
30229         this.xhr.onerror = function()
30230         {
30231             _this.xhrOnError(_this.xhr);
30232         }
30233         
30234         var formData = new FormData();
30235
30236         formData.append('returnHTML', 'NO');
30237         
30238         if(crop){
30239             formData.append('crop', crop);
30240         }
30241         
30242         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30243             formData.append(this.paramName, file, file.name);
30244         }
30245         
30246         if(typeof(file.filename) != 'undefined'){
30247             formData.append('filename', file.filename);
30248         }
30249         
30250         if(typeof(file.mimetype) != 'undefined'){
30251             formData.append('mimetype', file.mimetype);
30252         }
30253         
30254         if(this.fireEvent('arrange', this, formData) != false){
30255             this.xhr.send(formData);
30256         };
30257     },
30258     
30259     xhrOnLoad : function(xhr)
30260     {
30261         if(this.loadMask){
30262             this.maskEl.unmask();
30263         }
30264         
30265         if (xhr.readyState !== 4) {
30266             this.fireEvent('exception', this, xhr);
30267             return;
30268         }
30269
30270         var response = Roo.decode(xhr.responseText);
30271         
30272         if(!response.success){
30273             this.fireEvent('exception', this, xhr);
30274             return;
30275         }
30276         
30277         var response = Roo.decode(xhr.responseText);
30278         
30279         this.fireEvent('upload', this, response);
30280         
30281     },
30282     
30283     xhrOnError : function()
30284     {
30285         if(this.loadMask){
30286             this.maskEl.unmask();
30287         }
30288         
30289         Roo.log('xhr on error');
30290         
30291         var response = Roo.decode(xhr.responseText);
30292           
30293         Roo.log(response);
30294         
30295     },
30296     
30297     prepare : function(file)
30298     {   
30299         if(this.loadMask){
30300             this.maskEl.mask(this.loadingText);
30301         }
30302         
30303         this.file = false;
30304         this.exif = {};
30305         
30306         if(typeof(file) === 'string'){
30307             this.loadCanvas(file);
30308             return;
30309         }
30310         
30311         if(!file || !this.urlAPI){
30312             return;
30313         }
30314         
30315         this.file = file;
30316         this.cropType = file.type;
30317         
30318         var _this = this;
30319         
30320         if(this.fireEvent('prepare', this, this.file) != false){
30321             
30322             var reader = new FileReader();
30323             
30324             reader.onload = function (e) {
30325                 if (e.target.error) {
30326                     Roo.log(e.target.error);
30327                     return;
30328                 }
30329                 
30330                 var buffer = e.target.result,
30331                     dataView = new DataView(buffer),
30332                     offset = 2,
30333                     maxOffset = dataView.byteLength - 4,
30334                     markerBytes,
30335                     markerLength;
30336                 
30337                 if (dataView.getUint16(0) === 0xffd8) {
30338                     while (offset < maxOffset) {
30339                         markerBytes = dataView.getUint16(offset);
30340                         
30341                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30342                             markerLength = dataView.getUint16(offset + 2) + 2;
30343                             if (offset + markerLength > dataView.byteLength) {
30344                                 Roo.log('Invalid meta data: Invalid segment size.');
30345                                 break;
30346                             }
30347                             
30348                             if(markerBytes == 0xffe1){
30349                                 _this.parseExifData(
30350                                     dataView,
30351                                     offset,
30352                                     markerLength
30353                                 );
30354                             }
30355                             
30356                             offset += markerLength;
30357                             
30358                             continue;
30359                         }
30360                         
30361                         break;
30362                     }
30363                     
30364                 }
30365                 
30366                 var url = _this.urlAPI.createObjectURL(_this.file);
30367                 
30368                 _this.loadCanvas(url);
30369                 
30370                 return;
30371             }
30372             
30373             reader.readAsArrayBuffer(this.file);
30374             
30375         }
30376         
30377     },
30378     
30379     parseExifData : function(dataView, offset, length)
30380     {
30381         var tiffOffset = offset + 10,
30382             littleEndian,
30383             dirOffset;
30384     
30385         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30386             // No Exif data, might be XMP data instead
30387             return;
30388         }
30389         
30390         // Check for the ASCII code for "Exif" (0x45786966):
30391         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30392             // No Exif data, might be XMP data instead
30393             return;
30394         }
30395         if (tiffOffset + 8 > dataView.byteLength) {
30396             Roo.log('Invalid Exif data: Invalid segment size.');
30397             return;
30398         }
30399         // Check for the two null bytes:
30400         if (dataView.getUint16(offset + 8) !== 0x0000) {
30401             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30402             return;
30403         }
30404         // Check the byte alignment:
30405         switch (dataView.getUint16(tiffOffset)) {
30406         case 0x4949:
30407             littleEndian = true;
30408             break;
30409         case 0x4D4D:
30410             littleEndian = false;
30411             break;
30412         default:
30413             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30414             return;
30415         }
30416         // Check for the TIFF tag marker (0x002A):
30417         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30418             Roo.log('Invalid Exif data: Missing TIFF marker.');
30419             return;
30420         }
30421         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30422         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30423         
30424         this.parseExifTags(
30425             dataView,
30426             tiffOffset,
30427             tiffOffset + dirOffset,
30428             littleEndian
30429         );
30430     },
30431     
30432     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30433     {
30434         var tagsNumber,
30435             dirEndOffset,
30436             i;
30437         if (dirOffset + 6 > dataView.byteLength) {
30438             Roo.log('Invalid Exif data: Invalid directory offset.');
30439             return;
30440         }
30441         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30442         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30443         if (dirEndOffset + 4 > dataView.byteLength) {
30444             Roo.log('Invalid Exif data: Invalid directory size.');
30445             return;
30446         }
30447         for (i = 0; i < tagsNumber; i += 1) {
30448             this.parseExifTag(
30449                 dataView,
30450                 tiffOffset,
30451                 dirOffset + 2 + 12 * i, // tag offset
30452                 littleEndian
30453             );
30454         }
30455         // Return the offset to the next directory:
30456         return dataView.getUint32(dirEndOffset, littleEndian);
30457     },
30458     
30459     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30460     {
30461         var tag = dataView.getUint16(offset, littleEndian);
30462         
30463         this.exif[tag] = this.getExifValue(
30464             dataView,
30465             tiffOffset,
30466             offset,
30467             dataView.getUint16(offset + 2, littleEndian), // tag type
30468             dataView.getUint32(offset + 4, littleEndian), // tag length
30469             littleEndian
30470         );
30471     },
30472     
30473     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30474     {
30475         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30476             tagSize,
30477             dataOffset,
30478             values,
30479             i,
30480             str,
30481             c;
30482     
30483         if (!tagType) {
30484             Roo.log('Invalid Exif data: Invalid tag type.');
30485             return;
30486         }
30487         
30488         tagSize = tagType.size * length;
30489         // Determine if the value is contained in the dataOffset bytes,
30490         // or if the value at the dataOffset is a pointer to the actual data:
30491         dataOffset = tagSize > 4 ?
30492                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30493         if (dataOffset + tagSize > dataView.byteLength) {
30494             Roo.log('Invalid Exif data: Invalid data offset.');
30495             return;
30496         }
30497         if (length === 1) {
30498             return tagType.getValue(dataView, dataOffset, littleEndian);
30499         }
30500         values = [];
30501         for (i = 0; i < length; i += 1) {
30502             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30503         }
30504         
30505         if (tagType.ascii) {
30506             str = '';
30507             // Concatenate the chars:
30508             for (i = 0; i < values.length; i += 1) {
30509                 c = values[i];
30510                 // Ignore the terminating NULL byte(s):
30511                 if (c === '\u0000') {
30512                     break;
30513                 }
30514                 str += c;
30515             }
30516             return str;
30517         }
30518         return values;
30519     }
30520     
30521 });
30522
30523 Roo.apply(Roo.bootstrap.UploadCropbox, {
30524     tags : {
30525         'Orientation': 0x0112
30526     },
30527     
30528     Orientation: {
30529             1: 0, //'top-left',
30530 //            2: 'top-right',
30531             3: 180, //'bottom-right',
30532 //            4: 'bottom-left',
30533 //            5: 'left-top',
30534             6: 90, //'right-top',
30535 //            7: 'right-bottom',
30536             8: 270 //'left-bottom'
30537     },
30538     
30539     exifTagTypes : {
30540         // byte, 8-bit unsigned int:
30541         1: {
30542             getValue: function (dataView, dataOffset) {
30543                 return dataView.getUint8(dataOffset);
30544             },
30545             size: 1
30546         },
30547         // ascii, 8-bit byte:
30548         2: {
30549             getValue: function (dataView, dataOffset) {
30550                 return String.fromCharCode(dataView.getUint8(dataOffset));
30551             },
30552             size: 1,
30553             ascii: true
30554         },
30555         // short, 16 bit int:
30556         3: {
30557             getValue: function (dataView, dataOffset, littleEndian) {
30558                 return dataView.getUint16(dataOffset, littleEndian);
30559             },
30560             size: 2
30561         },
30562         // long, 32 bit int:
30563         4: {
30564             getValue: function (dataView, dataOffset, littleEndian) {
30565                 return dataView.getUint32(dataOffset, littleEndian);
30566             },
30567             size: 4
30568         },
30569         // rational = two long values, first is numerator, second is denominator:
30570         5: {
30571             getValue: function (dataView, dataOffset, littleEndian) {
30572                 return dataView.getUint32(dataOffset, littleEndian) /
30573                     dataView.getUint32(dataOffset + 4, littleEndian);
30574             },
30575             size: 8
30576         },
30577         // slong, 32 bit signed int:
30578         9: {
30579             getValue: function (dataView, dataOffset, littleEndian) {
30580                 return dataView.getInt32(dataOffset, littleEndian);
30581             },
30582             size: 4
30583         },
30584         // srational, two slongs, first is numerator, second is denominator:
30585         10: {
30586             getValue: function (dataView, dataOffset, littleEndian) {
30587                 return dataView.getInt32(dataOffset, littleEndian) /
30588                     dataView.getInt32(dataOffset + 4, littleEndian);
30589             },
30590             size: 8
30591         }
30592     },
30593     
30594     footer : {
30595         STANDARD : [
30596             {
30597                 tag : 'div',
30598                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30599                 action : 'rotate-left',
30600                 cn : [
30601                     {
30602                         tag : 'button',
30603                         cls : 'btn btn-default',
30604                         html : '<i class="fa fa-undo"></i>'
30605                     }
30606                 ]
30607             },
30608             {
30609                 tag : 'div',
30610                 cls : 'btn-group roo-upload-cropbox-picture',
30611                 action : 'picture',
30612                 cn : [
30613                     {
30614                         tag : 'button',
30615                         cls : 'btn btn-default',
30616                         html : '<i class="fa fa-picture-o"></i>'
30617                     }
30618                 ]
30619             },
30620             {
30621                 tag : 'div',
30622                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30623                 action : 'rotate-right',
30624                 cn : [
30625                     {
30626                         tag : 'button',
30627                         cls : 'btn btn-default',
30628                         html : '<i class="fa fa-repeat"></i>'
30629                     }
30630                 ]
30631             }
30632         ],
30633         DOCUMENT : [
30634             {
30635                 tag : 'div',
30636                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30637                 action : 'rotate-left',
30638                 cn : [
30639                     {
30640                         tag : 'button',
30641                         cls : 'btn btn-default',
30642                         html : '<i class="fa fa-undo"></i>'
30643                     }
30644                 ]
30645             },
30646             {
30647                 tag : 'div',
30648                 cls : 'btn-group roo-upload-cropbox-download',
30649                 action : 'download',
30650                 cn : [
30651                     {
30652                         tag : 'button',
30653                         cls : 'btn btn-default',
30654                         html : '<i class="fa fa-download"></i>'
30655                     }
30656                 ]
30657             },
30658             {
30659                 tag : 'div',
30660                 cls : 'btn-group roo-upload-cropbox-crop',
30661                 action : 'crop',
30662                 cn : [
30663                     {
30664                         tag : 'button',
30665                         cls : 'btn btn-default',
30666                         html : '<i class="fa fa-crop"></i>'
30667                     }
30668                 ]
30669             },
30670             {
30671                 tag : 'div',
30672                 cls : 'btn-group roo-upload-cropbox-trash',
30673                 action : 'trash',
30674                 cn : [
30675                     {
30676                         tag : 'button',
30677                         cls : 'btn btn-default',
30678                         html : '<i class="fa fa-trash"></i>'
30679                     }
30680                 ]
30681             },
30682             {
30683                 tag : 'div',
30684                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30685                 action : 'rotate-right',
30686                 cn : [
30687                     {
30688                         tag : 'button',
30689                         cls : 'btn btn-default',
30690                         html : '<i class="fa fa-repeat"></i>'
30691                     }
30692                 ]
30693             }
30694         ],
30695         ROTATOR : [
30696             {
30697                 tag : 'div',
30698                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30699                 action : 'rotate-left',
30700                 cn : [
30701                     {
30702                         tag : 'button',
30703                         cls : 'btn btn-default',
30704                         html : '<i class="fa fa-undo"></i>'
30705                     }
30706                 ]
30707             },
30708             {
30709                 tag : 'div',
30710                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30711                 action : 'rotate-right',
30712                 cn : [
30713                     {
30714                         tag : 'button',
30715                         cls : 'btn btn-default',
30716                         html : '<i class="fa fa-repeat"></i>'
30717                     }
30718                 ]
30719             }
30720         ]
30721     }
30722 });
30723
30724 /*
30725 * Licence: LGPL
30726 */
30727
30728 /**
30729  * @class Roo.bootstrap.DocumentManager
30730  * @extends Roo.bootstrap.Component
30731  * Bootstrap DocumentManager class
30732  * @cfg {String} paramName default 'imageUpload'
30733  * @cfg {String} toolTipName default 'filename'
30734  * @cfg {String} method default POST
30735  * @cfg {String} url action url
30736  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30737  * @cfg {Boolean} multiple multiple upload default true
30738  * @cfg {Number} thumbSize default 300
30739  * @cfg {String} fieldLabel
30740  * @cfg {Number} labelWidth default 4
30741  * @cfg {String} labelAlign (left|top) default left
30742  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30743 * @cfg {Number} labellg set the width of label (1-12)
30744  * @cfg {Number} labelmd set the width of label (1-12)
30745  * @cfg {Number} labelsm set the width of label (1-12)
30746  * @cfg {Number} labelxs set the width of label (1-12)
30747  * 
30748  * @constructor
30749  * Create a new DocumentManager
30750  * @param {Object} config The config object
30751  */
30752
30753 Roo.bootstrap.DocumentManager = function(config){
30754     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30755     
30756     this.files = [];
30757     this.delegates = [];
30758     
30759     this.addEvents({
30760         /**
30761          * @event initial
30762          * Fire when initial the DocumentManager
30763          * @param {Roo.bootstrap.DocumentManager} this
30764          */
30765         "initial" : true,
30766         /**
30767          * @event inspect
30768          * inspect selected file
30769          * @param {Roo.bootstrap.DocumentManager} this
30770          * @param {File} file
30771          */
30772         "inspect" : true,
30773         /**
30774          * @event exception
30775          * Fire when xhr load exception
30776          * @param {Roo.bootstrap.DocumentManager} this
30777          * @param {XMLHttpRequest} xhr
30778          */
30779         "exception" : true,
30780         /**
30781          * @event afterupload
30782          * Fire when xhr load exception
30783          * @param {Roo.bootstrap.DocumentManager} this
30784          * @param {XMLHttpRequest} xhr
30785          */
30786         "afterupload" : true,
30787         /**
30788          * @event prepare
30789          * prepare the form data
30790          * @param {Roo.bootstrap.DocumentManager} this
30791          * @param {Object} formData
30792          */
30793         "prepare" : true,
30794         /**
30795          * @event remove
30796          * Fire when remove the file
30797          * @param {Roo.bootstrap.DocumentManager} this
30798          * @param {Object} file
30799          */
30800         "remove" : true,
30801         /**
30802          * @event refresh
30803          * Fire after refresh the file
30804          * @param {Roo.bootstrap.DocumentManager} this
30805          */
30806         "refresh" : true,
30807         /**
30808          * @event click
30809          * Fire after click the image
30810          * @param {Roo.bootstrap.DocumentManager} this
30811          * @param {Object} file
30812          */
30813         "click" : true,
30814         /**
30815          * @event edit
30816          * Fire when upload a image and editable set to true
30817          * @param {Roo.bootstrap.DocumentManager} this
30818          * @param {Object} file
30819          */
30820         "edit" : true,
30821         /**
30822          * @event beforeselectfile
30823          * Fire before select file
30824          * @param {Roo.bootstrap.DocumentManager} this
30825          */
30826         "beforeselectfile" : true,
30827         /**
30828          * @event process
30829          * Fire before process file
30830          * @param {Roo.bootstrap.DocumentManager} this
30831          * @param {Object} file
30832          */
30833         "process" : true,
30834         /**
30835          * @event previewrendered
30836          * Fire when preview rendered
30837          * @param {Roo.bootstrap.DocumentManager} this
30838          * @param {Object} file
30839          */
30840         "previewrendered" : true,
30841         /**
30842          */
30843         "previewResize" : true
30844         
30845     });
30846 };
30847
30848 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
30849     
30850     boxes : 0,
30851     inputName : '',
30852     thumbSize : 300,
30853     multiple : true,
30854     files : false,
30855     method : 'POST',
30856     url : '',
30857     paramName : 'imageUpload',
30858     toolTipName : 'filename',
30859     fieldLabel : '',
30860     labelWidth : 4,
30861     labelAlign : 'left',
30862     editable : true,
30863     delegates : false,
30864     xhr : false, 
30865     
30866     labellg : 0,
30867     labelmd : 0,
30868     labelsm : 0,
30869     labelxs : 0,
30870     
30871     getAutoCreate : function()
30872     {   
30873         var managerWidget = {
30874             tag : 'div',
30875             cls : 'roo-document-manager',
30876             cn : [
30877                 {
30878                     tag : 'input',
30879                     cls : 'roo-document-manager-selector',
30880                     type : 'file'
30881                 },
30882                 {
30883                     tag : 'div',
30884                     cls : 'roo-document-manager-uploader',
30885                     cn : [
30886                         {
30887                             tag : 'div',
30888                             cls : 'roo-document-manager-upload-btn',
30889                             html : '<i class="fa fa-plus"></i>'
30890                         }
30891                     ]
30892                     
30893                 }
30894             ]
30895         };
30896         
30897         var content = [
30898             {
30899                 tag : 'div',
30900                 cls : 'column col-md-12',
30901                 cn : managerWidget
30902             }
30903         ];
30904         
30905         if(this.fieldLabel.length){
30906             
30907             content = [
30908                 {
30909                     tag : 'div',
30910                     cls : 'column col-md-12',
30911                     html : this.fieldLabel
30912                 },
30913                 {
30914                     tag : 'div',
30915                     cls : 'column col-md-12',
30916                     cn : managerWidget
30917                 }
30918             ];
30919
30920             if(this.labelAlign == 'left'){
30921                 content = [
30922                     {
30923                         tag : 'div',
30924                         cls : 'column',
30925                         html : this.fieldLabel
30926                     },
30927                     {
30928                         tag : 'div',
30929                         cls : 'column',
30930                         cn : managerWidget
30931                     }
30932                 ];
30933                 
30934                 if(this.labelWidth > 12){
30935                     content[0].style = "width: " + this.labelWidth + 'px';
30936                 }
30937
30938                 if(this.labelWidth < 13 && this.labelmd == 0){
30939                     this.labelmd = this.labelWidth;
30940                 }
30941
30942                 if(this.labellg > 0){
30943                     content[0].cls += ' col-lg-' + this.labellg;
30944                     content[1].cls += ' col-lg-' + (12 - this.labellg);
30945                 }
30946
30947                 if(this.labelmd > 0){
30948                     content[0].cls += ' col-md-' + this.labelmd;
30949                     content[1].cls += ' col-md-' + (12 - this.labelmd);
30950                 }
30951
30952                 if(this.labelsm > 0){
30953                     content[0].cls += ' col-sm-' + this.labelsm;
30954                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
30955                 }
30956
30957                 if(this.labelxs > 0){
30958                     content[0].cls += ' col-xs-' + this.labelxs;
30959                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
30960                 }
30961                 
30962             }
30963         }
30964         
30965         var cfg = {
30966             tag : 'div',
30967             cls : 'row clearfix',
30968             cn : content
30969         };
30970         
30971         return cfg;
30972         
30973     },
30974     
30975     initEvents : function()
30976     {
30977         this.managerEl = this.el.select('.roo-document-manager', true).first();
30978         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30979         
30980         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
30981         this.selectorEl.hide();
30982         
30983         if(this.multiple){
30984             this.selectorEl.attr('multiple', 'multiple');
30985         }
30986         
30987         this.selectorEl.on('change', this.onFileSelected, this);
30988         
30989         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
30990         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30991         
30992         this.uploader.on('click', this.onUploaderClick, this);
30993         
30994         this.renderProgressDialog();
30995         
30996         var _this = this;
30997         
30998         window.addEventListener("resize", function() { _this.refresh(); } );
30999         
31000         this.fireEvent('initial', this);
31001     },
31002     
31003     renderProgressDialog : function()
31004     {
31005         var _this = this;
31006         
31007         this.progressDialog = new Roo.bootstrap.Modal({
31008             cls : 'roo-document-manager-progress-dialog',
31009             allow_close : false,
31010             animate : false,
31011             title : '',
31012             buttons : [
31013                 {
31014                     name  :'cancel',
31015                     weight : 'danger',
31016                     html : 'Cancel'
31017                 }
31018             ], 
31019             listeners : { 
31020                 btnclick : function() {
31021                     _this.uploadCancel();
31022                     this.hide();
31023                 }
31024             }
31025         });
31026          
31027         this.progressDialog.render(Roo.get(document.body));
31028          
31029         this.progress = new Roo.bootstrap.Progress({
31030             cls : 'roo-document-manager-progress',
31031             active : true,
31032             striped : true
31033         });
31034         
31035         this.progress.render(this.progressDialog.getChildContainer());
31036         
31037         this.progressBar = new Roo.bootstrap.ProgressBar({
31038             cls : 'roo-document-manager-progress-bar',
31039             aria_valuenow : 0,
31040             aria_valuemin : 0,
31041             aria_valuemax : 12,
31042             panel : 'success'
31043         });
31044         
31045         this.progressBar.render(this.progress.getChildContainer());
31046     },
31047     
31048     onUploaderClick : function(e)
31049     {
31050         e.preventDefault();
31051      
31052         if(this.fireEvent('beforeselectfile', this) != false){
31053             this.selectorEl.dom.click();
31054         }
31055         
31056     },
31057     
31058     onFileSelected : function(e)
31059     {
31060         e.preventDefault();
31061         
31062         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31063             return;
31064         }
31065         
31066         Roo.each(this.selectorEl.dom.files, function(file){
31067             if(this.fireEvent('inspect', this, file) != false){
31068                 this.files.push(file);
31069             }
31070         }, this);
31071         
31072         this.queue();
31073         
31074     },
31075     
31076     queue : function()
31077     {
31078         this.selectorEl.dom.value = '';
31079         
31080         if(!this.files || !this.files.length){
31081             return;
31082         }
31083         
31084         if(this.boxes > 0 && this.files.length > this.boxes){
31085             this.files = this.files.slice(0, this.boxes);
31086         }
31087         
31088         this.uploader.show();
31089         
31090         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31091             this.uploader.hide();
31092         }
31093         
31094         var _this = this;
31095         
31096         var files = [];
31097         
31098         var docs = [];
31099         
31100         Roo.each(this.files, function(file){
31101             
31102             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31103                 var f = this.renderPreview(file);
31104                 files.push(f);
31105                 return;
31106             }
31107             
31108             if(file.type.indexOf('image') != -1){
31109                 this.delegates.push(
31110                     (function(){
31111                         _this.process(file);
31112                     }).createDelegate(this)
31113                 );
31114         
31115                 return;
31116             }
31117             
31118             docs.push(
31119                 (function(){
31120                     _this.process(file);
31121                 }).createDelegate(this)
31122             );
31123             
31124         }, this);
31125         
31126         this.files = files;
31127         
31128         this.delegates = this.delegates.concat(docs);
31129         
31130         if(!this.delegates.length){
31131             this.refresh();
31132             return;
31133         }
31134         
31135         this.progressBar.aria_valuemax = this.delegates.length;
31136         
31137         this.arrange();
31138         
31139         return;
31140     },
31141     
31142     arrange : function()
31143     {
31144         if(!this.delegates.length){
31145             this.progressDialog.hide();
31146             this.refresh();
31147             return;
31148         }
31149         
31150         var delegate = this.delegates.shift();
31151         
31152         this.progressDialog.show();
31153         
31154         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31155         
31156         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31157         
31158         delegate();
31159     },
31160     
31161     refresh : function()
31162     {
31163         this.uploader.show();
31164         
31165         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31166             this.uploader.hide();
31167         }
31168         
31169         Roo.isTouch ? this.closable(false) : this.closable(true);
31170         
31171         this.fireEvent('refresh', this);
31172     },
31173     
31174     onRemove : function(e, el, o)
31175     {
31176         e.preventDefault();
31177         
31178         this.fireEvent('remove', this, o);
31179         
31180     },
31181     
31182     remove : function(o)
31183     {
31184         var files = [];
31185         
31186         Roo.each(this.files, function(file){
31187             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31188                 files.push(file);
31189                 return;
31190             }
31191
31192             o.target.remove();
31193
31194         }, this);
31195         
31196         this.files = files;
31197         
31198         this.refresh();
31199     },
31200     
31201     clear : function()
31202     {
31203         Roo.each(this.files, function(file){
31204             if(!file.target){
31205                 return;
31206             }
31207             
31208             file.target.remove();
31209
31210         }, this);
31211         
31212         this.files = [];
31213         
31214         this.refresh();
31215     },
31216     
31217     onClick : function(e, el, o)
31218     {
31219         e.preventDefault();
31220         
31221         this.fireEvent('click', this, o);
31222         
31223     },
31224     
31225     closable : function(closable)
31226     {
31227         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31228             
31229             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31230             
31231             if(closable){
31232                 el.show();
31233                 return;
31234             }
31235             
31236             el.hide();
31237             
31238         }, this);
31239     },
31240     
31241     xhrOnLoad : function(xhr)
31242     {
31243         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31244             el.remove();
31245         }, this);
31246         
31247         if (xhr.readyState !== 4) {
31248             this.arrange();
31249             this.fireEvent('exception', this, xhr);
31250             return;
31251         }
31252
31253         var response = Roo.decode(xhr.responseText);
31254         
31255         if(!response.success){
31256             this.arrange();
31257             this.fireEvent('exception', this, xhr);
31258             return;
31259         }
31260         
31261         var file = this.renderPreview(response.data);
31262         
31263         this.files.push(file);
31264         
31265         this.arrange();
31266         
31267         this.fireEvent('afterupload', this, xhr);
31268         
31269     },
31270     
31271     xhrOnError : function(xhr)
31272     {
31273         Roo.log('xhr on error');
31274         
31275         var response = Roo.decode(xhr.responseText);
31276           
31277         Roo.log(response);
31278         
31279         this.arrange();
31280     },
31281     
31282     process : function(file)
31283     {
31284         if(this.fireEvent('process', this, file) !== false){
31285             if(this.editable && file.type.indexOf('image') != -1){
31286                 this.fireEvent('edit', this, file);
31287                 return;
31288             }
31289
31290             this.uploadStart(file, false);
31291
31292             return;
31293         }
31294         
31295     },
31296     
31297     uploadStart : function(file, crop)
31298     {
31299         this.xhr = new XMLHttpRequest();
31300         
31301         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31302             this.arrange();
31303             return;
31304         }
31305         
31306         file.xhr = this.xhr;
31307             
31308         this.managerEl.createChild({
31309             tag : 'div',
31310             cls : 'roo-document-manager-loading',
31311             cn : [
31312                 {
31313                     tag : 'div',
31314                     tooltip : file.name,
31315                     cls : 'roo-document-manager-thumb',
31316                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31317                 }
31318             ]
31319
31320         });
31321
31322         this.xhr.open(this.method, this.url, true);
31323         
31324         var headers = {
31325             "Accept": "application/json",
31326             "Cache-Control": "no-cache",
31327             "X-Requested-With": "XMLHttpRequest"
31328         };
31329         
31330         for (var headerName in headers) {
31331             var headerValue = headers[headerName];
31332             if (headerValue) {
31333                 this.xhr.setRequestHeader(headerName, headerValue);
31334             }
31335         }
31336         
31337         var _this = this;
31338         
31339         this.xhr.onload = function()
31340         {
31341             _this.xhrOnLoad(_this.xhr);
31342         }
31343         
31344         this.xhr.onerror = function()
31345         {
31346             _this.xhrOnError(_this.xhr);
31347         }
31348         
31349         var formData = new FormData();
31350
31351         formData.append('returnHTML', 'NO');
31352         
31353         if(crop){
31354             formData.append('crop', crop);
31355         }
31356         
31357         formData.append(this.paramName, file, file.name);
31358         
31359         var options = {
31360             file : file, 
31361             manually : false
31362         };
31363         
31364         if(this.fireEvent('prepare', this, formData, options) != false){
31365             
31366             if(options.manually){
31367                 return;
31368             }
31369             
31370             this.xhr.send(formData);
31371             return;
31372         };
31373         
31374         this.uploadCancel();
31375     },
31376     
31377     uploadCancel : function()
31378     {
31379         if (this.xhr) {
31380             this.xhr.abort();
31381         }
31382         
31383         this.delegates = [];
31384         
31385         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31386             el.remove();
31387         }, this);
31388         
31389         this.arrange();
31390     },
31391     
31392     renderPreview : function(file)
31393     {
31394         if(typeof(file.target) != 'undefined' && file.target){
31395             return file;
31396         }
31397         
31398         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31399         
31400         var previewEl = this.managerEl.createChild({
31401             tag : 'div',
31402             cls : 'roo-document-manager-preview',
31403             cn : [
31404                 {
31405                     tag : 'div',
31406                     tooltip : file[this.toolTipName],
31407                     cls : 'roo-document-manager-thumb',
31408                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31409                 },
31410                 {
31411                     tag : 'button',
31412                     cls : 'close',
31413                     html : '<i class="fa fa-times-circle"></i>'
31414                 }
31415             ]
31416         });
31417
31418         var close = previewEl.select('button.close', true).first();
31419
31420         close.on('click', this.onRemove, this, file);
31421
31422         file.target = previewEl;
31423
31424         var image = previewEl.select('img', true).first();
31425         
31426         var _this = this;
31427         
31428         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31429         
31430         image.on('click', this.onClick, this, file);
31431         
31432         this.fireEvent('previewrendered', this, file);
31433         
31434         return file;
31435         
31436     },
31437     
31438     onPreviewLoad : function(file, image)
31439     {
31440         if(typeof(file.target) == 'undefined' || !file.target){
31441             return;
31442         }
31443         
31444         var width = image.dom.naturalWidth || image.dom.width;
31445         var height = image.dom.naturalHeight || image.dom.height;
31446         
31447         if(!this.previewResize) {
31448             return;
31449         }
31450         
31451         if(width > height){
31452             file.target.addClass('wide');
31453             return;
31454         }
31455         
31456         file.target.addClass('tall');
31457         return;
31458         
31459     },
31460     
31461     uploadFromSource : function(file, crop)
31462     {
31463         this.xhr = new XMLHttpRequest();
31464         
31465         this.managerEl.createChild({
31466             tag : 'div',
31467             cls : 'roo-document-manager-loading',
31468             cn : [
31469                 {
31470                     tag : 'div',
31471                     tooltip : file.name,
31472                     cls : 'roo-document-manager-thumb',
31473                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31474                 }
31475             ]
31476
31477         });
31478
31479         this.xhr.open(this.method, this.url, true);
31480         
31481         var headers = {
31482             "Accept": "application/json",
31483             "Cache-Control": "no-cache",
31484             "X-Requested-With": "XMLHttpRequest"
31485         };
31486         
31487         for (var headerName in headers) {
31488             var headerValue = headers[headerName];
31489             if (headerValue) {
31490                 this.xhr.setRequestHeader(headerName, headerValue);
31491             }
31492         }
31493         
31494         var _this = this;
31495         
31496         this.xhr.onload = function()
31497         {
31498             _this.xhrOnLoad(_this.xhr);
31499         }
31500         
31501         this.xhr.onerror = function()
31502         {
31503             _this.xhrOnError(_this.xhr);
31504         }
31505         
31506         var formData = new FormData();
31507
31508         formData.append('returnHTML', 'NO');
31509         
31510         formData.append('crop', crop);
31511         
31512         if(typeof(file.filename) != 'undefined'){
31513             formData.append('filename', file.filename);
31514         }
31515         
31516         if(typeof(file.mimetype) != 'undefined'){
31517             formData.append('mimetype', file.mimetype);
31518         }
31519         
31520         Roo.log(formData);
31521         
31522         if(this.fireEvent('prepare', this, formData) != false){
31523             this.xhr.send(formData);
31524         };
31525     }
31526 });
31527
31528 /*
31529 * Licence: LGPL
31530 */
31531
31532 /**
31533  * @class Roo.bootstrap.DocumentViewer
31534  * @extends Roo.bootstrap.Component
31535  * Bootstrap DocumentViewer class
31536  * @cfg {Boolean} showDownload (true|false) show download button (default true)
31537  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31538  * 
31539  * @constructor
31540  * Create a new DocumentViewer
31541  * @param {Object} config The config object
31542  */
31543
31544 Roo.bootstrap.DocumentViewer = function(config){
31545     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31546     
31547     this.addEvents({
31548         /**
31549          * @event initial
31550          * Fire after initEvent
31551          * @param {Roo.bootstrap.DocumentViewer} this
31552          */
31553         "initial" : true,
31554         /**
31555          * @event click
31556          * Fire after click
31557          * @param {Roo.bootstrap.DocumentViewer} this
31558          */
31559         "click" : true,
31560         /**
31561          * @event download
31562          * Fire after download button
31563          * @param {Roo.bootstrap.DocumentViewer} this
31564          */
31565         "download" : true,
31566         /**
31567          * @event trash
31568          * Fire after trash button
31569          * @param {Roo.bootstrap.DocumentViewer} this
31570          */
31571         "trash" : true
31572         
31573     });
31574 };
31575
31576 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
31577     
31578     showDownload : true,
31579     
31580     showTrash : true,
31581     
31582     getAutoCreate : function()
31583     {
31584         var cfg = {
31585             tag : 'div',
31586             cls : 'roo-document-viewer',
31587             cn : [
31588                 {
31589                     tag : 'div',
31590                     cls : 'roo-document-viewer-body',
31591                     cn : [
31592                         {
31593                             tag : 'div',
31594                             cls : 'roo-document-viewer-thumb',
31595                             cn : [
31596                                 {
31597                                     tag : 'img',
31598                                     cls : 'roo-document-viewer-image'
31599                                 }
31600                             ]
31601                         }
31602                     ]
31603                 },
31604                 {
31605                     tag : 'div',
31606                     cls : 'roo-document-viewer-footer',
31607                     cn : {
31608                         tag : 'div',
31609                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31610                         cn : [
31611                             {
31612                                 tag : 'div',
31613                                 cls : 'btn-group roo-document-viewer-download',
31614                                 cn : [
31615                                     {
31616                                         tag : 'button',
31617                                         cls : 'btn btn-default',
31618                                         html : '<i class="fa fa-download"></i>'
31619                                     }
31620                                 ]
31621                             },
31622                             {
31623                                 tag : 'div',
31624                                 cls : 'btn-group roo-document-viewer-trash',
31625                                 cn : [
31626                                     {
31627                                         tag : 'button',
31628                                         cls : 'btn btn-default',
31629                                         html : '<i class="fa fa-trash"></i>'
31630                                     }
31631                                 ]
31632                             }
31633                         ]
31634                     }
31635                 }
31636             ]
31637         };
31638         
31639         return cfg;
31640     },
31641     
31642     initEvents : function()
31643     {
31644         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31645         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31646         
31647         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31648         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31649         
31650         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31651         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31652         
31653         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31654         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31655         
31656         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31657         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31658         
31659         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31660         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31661         
31662         this.bodyEl.on('click', this.onClick, this);
31663         this.downloadBtn.on('click', this.onDownload, this);
31664         this.trashBtn.on('click', this.onTrash, this);
31665         
31666         this.downloadBtn.hide();
31667         this.trashBtn.hide();
31668         
31669         if(this.showDownload){
31670             this.downloadBtn.show();
31671         }
31672         
31673         if(this.showTrash){
31674             this.trashBtn.show();
31675         }
31676         
31677         if(!this.showDownload && !this.showTrash) {
31678             this.footerEl.hide();
31679         }
31680         
31681     },
31682     
31683     initial : function()
31684     {
31685         this.fireEvent('initial', this);
31686         
31687     },
31688     
31689     onClick : function(e)
31690     {
31691         e.preventDefault();
31692         
31693         this.fireEvent('click', this);
31694     },
31695     
31696     onDownload : function(e)
31697     {
31698         e.preventDefault();
31699         
31700         this.fireEvent('download', this);
31701     },
31702     
31703     onTrash : function(e)
31704     {
31705         e.preventDefault();
31706         
31707         this.fireEvent('trash', this);
31708     }
31709     
31710 });
31711 /*
31712  * - LGPL
31713  *
31714  * nav progress bar
31715  * 
31716  */
31717
31718 /**
31719  * @class Roo.bootstrap.NavProgressBar
31720  * @extends Roo.bootstrap.Component
31721  * Bootstrap NavProgressBar class
31722  * 
31723  * @constructor
31724  * Create a new nav progress bar
31725  * @param {Object} config The config object
31726  */
31727
31728 Roo.bootstrap.NavProgressBar = function(config){
31729     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31730
31731     this.bullets = this.bullets || [];
31732    
31733 //    Roo.bootstrap.NavProgressBar.register(this);
31734      this.addEvents({
31735         /**
31736              * @event changed
31737              * Fires when the active item changes
31738              * @param {Roo.bootstrap.NavProgressBar} this
31739              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31740              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
31741          */
31742         'changed': true
31743      });
31744     
31745 };
31746
31747 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
31748     
31749     bullets : [],
31750     barItems : [],
31751     
31752     getAutoCreate : function()
31753     {
31754         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31755         
31756         cfg = {
31757             tag : 'div',
31758             cls : 'roo-navigation-bar-group',
31759             cn : [
31760                 {
31761                     tag : 'div',
31762                     cls : 'roo-navigation-top-bar'
31763                 },
31764                 {
31765                     tag : 'div',
31766                     cls : 'roo-navigation-bullets-bar',
31767                     cn : [
31768                         {
31769                             tag : 'ul',
31770                             cls : 'roo-navigation-bar'
31771                         }
31772                     ]
31773                 },
31774                 
31775                 {
31776                     tag : 'div',
31777                     cls : 'roo-navigation-bottom-bar'
31778                 }
31779             ]
31780             
31781         };
31782         
31783         return cfg;
31784         
31785     },
31786     
31787     initEvents: function() 
31788     {
31789         
31790     },
31791     
31792     onRender : function(ct, position) 
31793     {
31794         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31795         
31796         if(this.bullets.length){
31797             Roo.each(this.bullets, function(b){
31798                this.addItem(b);
31799             }, this);
31800         }
31801         
31802         this.format();
31803         
31804     },
31805     
31806     addItem : function(cfg)
31807     {
31808         var item = new Roo.bootstrap.NavProgressItem(cfg);
31809         
31810         item.parentId = this.id;
31811         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
31812         
31813         if(cfg.html){
31814             var top = new Roo.bootstrap.Element({
31815                 tag : 'div',
31816                 cls : 'roo-navigation-bar-text'
31817             });
31818             
31819             var bottom = new Roo.bootstrap.Element({
31820                 tag : 'div',
31821                 cls : 'roo-navigation-bar-text'
31822             });
31823             
31824             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
31825             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
31826             
31827             var topText = new Roo.bootstrap.Element({
31828                 tag : 'span',
31829                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
31830             });
31831             
31832             var bottomText = new Roo.bootstrap.Element({
31833                 tag : 'span',
31834                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
31835             });
31836             
31837             topText.onRender(top.el, null);
31838             bottomText.onRender(bottom.el, null);
31839             
31840             item.topEl = top;
31841             item.bottomEl = bottom;
31842         }
31843         
31844         this.barItems.push(item);
31845         
31846         return item;
31847     },
31848     
31849     getActive : function()
31850     {
31851         var active = false;
31852         
31853         Roo.each(this.barItems, function(v){
31854             
31855             if (!v.isActive()) {
31856                 return;
31857             }
31858             
31859             active = v;
31860             return false;
31861             
31862         });
31863         
31864         return active;
31865     },
31866     
31867     setActiveItem : function(item)
31868     {
31869         var prev = false;
31870         
31871         Roo.each(this.barItems, function(v){
31872             if (v.rid == item.rid) {
31873                 return ;
31874             }
31875             
31876             if (v.isActive()) {
31877                 v.setActive(false);
31878                 prev = v;
31879             }
31880         });
31881
31882         item.setActive(true);
31883         
31884         this.fireEvent('changed', this, item, prev);
31885     },
31886     
31887     getBarItem: function(rid)
31888     {
31889         var ret = false;
31890         
31891         Roo.each(this.barItems, function(e) {
31892             if (e.rid != rid) {
31893                 return;
31894             }
31895             
31896             ret =  e;
31897             return false;
31898         });
31899         
31900         return ret;
31901     },
31902     
31903     indexOfItem : function(item)
31904     {
31905         var index = false;
31906         
31907         Roo.each(this.barItems, function(v, i){
31908             
31909             if (v.rid != item.rid) {
31910                 return;
31911             }
31912             
31913             index = i;
31914             return false
31915         });
31916         
31917         return index;
31918     },
31919     
31920     setActiveNext : function()
31921     {
31922         var i = this.indexOfItem(this.getActive());
31923         
31924         if (i > this.barItems.length) {
31925             return;
31926         }
31927         
31928         this.setActiveItem(this.barItems[i+1]);
31929     },
31930     
31931     setActivePrev : function()
31932     {
31933         var i = this.indexOfItem(this.getActive());
31934         
31935         if (i  < 1) {
31936             return;
31937         }
31938         
31939         this.setActiveItem(this.barItems[i-1]);
31940     },
31941     
31942     format : function()
31943     {
31944         if(!this.barItems.length){
31945             return;
31946         }
31947      
31948         var width = 100 / this.barItems.length;
31949         
31950         Roo.each(this.barItems, function(i){
31951             i.el.setStyle('width', width + '%');
31952             i.topEl.el.setStyle('width', width + '%');
31953             i.bottomEl.el.setStyle('width', width + '%');
31954         }, this);
31955         
31956     }
31957     
31958 });
31959 /*
31960  * - LGPL
31961  *
31962  * Nav Progress Item
31963  * 
31964  */
31965
31966 /**
31967  * @class Roo.bootstrap.NavProgressItem
31968  * @extends Roo.bootstrap.Component
31969  * Bootstrap NavProgressItem class
31970  * @cfg {String} rid the reference id
31971  * @cfg {Boolean} active (true|false) Is item active default false
31972  * @cfg {Boolean} disabled (true|false) Is item active default false
31973  * @cfg {String} html
31974  * @cfg {String} position (top|bottom) text position default bottom
31975  * @cfg {String} icon show icon instead of number
31976  * 
31977  * @constructor
31978  * Create a new NavProgressItem
31979  * @param {Object} config The config object
31980  */
31981 Roo.bootstrap.NavProgressItem = function(config){
31982     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
31983     this.addEvents({
31984         // raw events
31985         /**
31986          * @event click
31987          * The raw click event for the entire grid.
31988          * @param {Roo.bootstrap.NavProgressItem} this
31989          * @param {Roo.EventObject} e
31990          */
31991         "click" : true
31992     });
31993    
31994 };
31995
31996 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
31997     
31998     rid : '',
31999     active : false,
32000     disabled : false,
32001     html : '',
32002     position : 'bottom',
32003     icon : false,
32004     
32005     getAutoCreate : function()
32006     {
32007         var iconCls = 'roo-navigation-bar-item-icon';
32008         
32009         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32010         
32011         var cfg = {
32012             tag: 'li',
32013             cls: 'roo-navigation-bar-item',
32014             cn : [
32015                 {
32016                     tag : 'i',
32017                     cls : iconCls
32018                 }
32019             ]
32020         };
32021         
32022         if(this.active){
32023             cfg.cls += ' active';
32024         }
32025         if(this.disabled){
32026             cfg.cls += ' disabled';
32027         }
32028         
32029         return cfg;
32030     },
32031     
32032     disable : function()
32033     {
32034         this.setDisabled(true);
32035     },
32036     
32037     enable : function()
32038     {
32039         this.setDisabled(false);
32040     },
32041     
32042     initEvents: function() 
32043     {
32044         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32045         
32046         this.iconEl.on('click', this.onClick, this);
32047     },
32048     
32049     onClick : function(e)
32050     {
32051         e.preventDefault();
32052         
32053         if(this.disabled){
32054             return;
32055         }
32056         
32057         if(this.fireEvent('click', this, e) === false){
32058             return;
32059         };
32060         
32061         this.parent().setActiveItem(this);
32062     },
32063     
32064     isActive: function () 
32065     {
32066         return this.active;
32067     },
32068     
32069     setActive : function(state)
32070     {
32071         if(this.active == state){
32072             return;
32073         }
32074         
32075         this.active = state;
32076         
32077         if (state) {
32078             this.el.addClass('active');
32079             return;
32080         }
32081         
32082         this.el.removeClass('active');
32083         
32084         return;
32085     },
32086     
32087     setDisabled : function(state)
32088     {
32089         if(this.disabled == state){
32090             return;
32091         }
32092         
32093         this.disabled = state;
32094         
32095         if (state) {
32096             this.el.addClass('disabled');
32097             return;
32098         }
32099         
32100         this.el.removeClass('disabled');
32101     },
32102     
32103     tooltipEl : function()
32104     {
32105         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32106     }
32107 });
32108  
32109
32110  /*
32111  * - LGPL
32112  *
32113  * FieldLabel
32114  * 
32115  */
32116
32117 /**
32118  * @class Roo.bootstrap.FieldLabel
32119  * @extends Roo.bootstrap.Component
32120  * Bootstrap FieldLabel class
32121  * @cfg {String} html contents of the element
32122  * @cfg {String} tag tag of the element default label
32123  * @cfg {String} cls class of the element
32124  * @cfg {String} target label target 
32125  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32126  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32127  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32128  * @cfg {String} iconTooltip default "This field is required"
32129  * @cfg {String} indicatorpos (left|right) default left
32130  * 
32131  * @constructor
32132  * Create a new FieldLabel
32133  * @param {Object} config The config object
32134  */
32135
32136 Roo.bootstrap.FieldLabel = function(config){
32137     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32138     
32139     this.addEvents({
32140             /**
32141              * @event invalid
32142              * Fires after the field has been marked as invalid.
32143              * @param {Roo.form.FieldLabel} this
32144              * @param {String} msg The validation message
32145              */
32146             invalid : true,
32147             /**
32148              * @event valid
32149              * Fires after the field has been validated with no errors.
32150              * @param {Roo.form.FieldLabel} this
32151              */
32152             valid : true
32153         });
32154 };
32155
32156 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32157     
32158     tag: 'label',
32159     cls: '',
32160     html: '',
32161     target: '',
32162     allowBlank : true,
32163     invalidClass : 'has-warning',
32164     validClass : 'has-success',
32165     iconTooltip : 'This field is required',
32166     indicatorpos : 'left',
32167     
32168     getAutoCreate : function(){
32169         
32170         var cls = "";
32171         if (!this.allowBlank) {
32172             cls  = "visible";
32173         }
32174         
32175         var cfg = {
32176             tag : this.tag,
32177             cls : 'roo-bootstrap-field-label ' + this.cls,
32178             for : this.target,
32179             cn : [
32180                 {
32181                     tag : 'i',
32182                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32183                     tooltip : this.iconTooltip
32184                 },
32185                 {
32186                     tag : 'span',
32187                     html : this.html
32188                 }
32189             ] 
32190         };
32191         
32192         if(this.indicatorpos == 'right'){
32193             var cfg = {
32194                 tag : this.tag,
32195                 cls : 'roo-bootstrap-field-label ' + this.cls,
32196                 for : this.target,
32197                 cn : [
32198                     {
32199                         tag : 'span',
32200                         html : this.html
32201                     },
32202                     {
32203                         tag : 'i',
32204                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32205                         tooltip : this.iconTooltip
32206                     }
32207                 ] 
32208             };
32209         }
32210         
32211         return cfg;
32212     },
32213     
32214     initEvents: function() 
32215     {
32216         Roo.bootstrap.Element.superclass.initEvents.call(this);
32217         
32218         this.indicator = this.indicatorEl();
32219         
32220         if(this.indicator){
32221             this.indicator.removeClass('visible');
32222             this.indicator.addClass('invisible');
32223         }
32224         
32225         Roo.bootstrap.FieldLabel.register(this);
32226     },
32227     
32228     indicatorEl : function()
32229     {
32230         var indicator = this.el.select('i.roo-required-indicator',true).first();
32231         
32232         if(!indicator){
32233             return false;
32234         }
32235         
32236         return indicator;
32237         
32238     },
32239     
32240     /**
32241      * Mark this field as valid
32242      */
32243     markValid : function()
32244     {
32245         if(this.indicator){
32246             this.indicator.removeClass('visible');
32247             this.indicator.addClass('invisible');
32248         }
32249         if (Roo.bootstrap.version == 3) {
32250             this.el.removeClass(this.invalidClass);
32251             this.el.addClass(this.validClass);
32252         } else {
32253             this.el.removeClass('is-invalid');
32254             this.el.addClass('is-valid');
32255         }
32256         
32257         
32258         this.fireEvent('valid', this);
32259     },
32260     
32261     /**
32262      * Mark this field as invalid
32263      * @param {String} msg The validation message
32264      */
32265     markInvalid : function(msg)
32266     {
32267         if(this.indicator){
32268             this.indicator.removeClass('invisible');
32269             this.indicator.addClass('visible');
32270         }
32271           if (Roo.bootstrap.version == 3) {
32272             this.el.removeClass(this.validClass);
32273             this.el.addClass(this.invalidClass);
32274         } else {
32275             this.el.removeClass('is-valid');
32276             this.el.addClass('is-invalid');
32277         }
32278         
32279         
32280         this.fireEvent('invalid', this, msg);
32281     }
32282     
32283    
32284 });
32285
32286 Roo.apply(Roo.bootstrap.FieldLabel, {
32287     
32288     groups: {},
32289     
32290      /**
32291     * register a FieldLabel Group
32292     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32293     */
32294     register : function(label)
32295     {
32296         if(this.groups.hasOwnProperty(label.target)){
32297             return;
32298         }
32299      
32300         this.groups[label.target] = label;
32301         
32302     },
32303     /**
32304     * fetch a FieldLabel Group based on the target
32305     * @param {string} target
32306     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32307     */
32308     get: function(target) {
32309         if (typeof(this.groups[target]) == 'undefined') {
32310             return false;
32311         }
32312         
32313         return this.groups[target] ;
32314     }
32315 });
32316
32317  
32318
32319  /*
32320  * - LGPL
32321  *
32322  * page DateSplitField.
32323  * 
32324  */
32325
32326
32327 /**
32328  * @class Roo.bootstrap.DateSplitField
32329  * @extends Roo.bootstrap.Component
32330  * Bootstrap DateSplitField class
32331  * @cfg {string} fieldLabel - the label associated
32332  * @cfg {Number} labelWidth set the width of label (0-12)
32333  * @cfg {String} labelAlign (top|left)
32334  * @cfg {Boolean} dayAllowBlank (true|false) default false
32335  * @cfg {Boolean} monthAllowBlank (true|false) default false
32336  * @cfg {Boolean} yearAllowBlank (true|false) default false
32337  * @cfg {string} dayPlaceholder 
32338  * @cfg {string} monthPlaceholder
32339  * @cfg {string} yearPlaceholder
32340  * @cfg {string} dayFormat default 'd'
32341  * @cfg {string} monthFormat default 'm'
32342  * @cfg {string} yearFormat default 'Y'
32343  * @cfg {Number} labellg set the width of label (1-12)
32344  * @cfg {Number} labelmd set the width of label (1-12)
32345  * @cfg {Number} labelsm set the width of label (1-12)
32346  * @cfg {Number} labelxs set the width of label (1-12)
32347
32348  *     
32349  * @constructor
32350  * Create a new DateSplitField
32351  * @param {Object} config The config object
32352  */
32353
32354 Roo.bootstrap.DateSplitField = function(config){
32355     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32356     
32357     this.addEvents({
32358         // raw events
32359          /**
32360          * @event years
32361          * getting the data of years
32362          * @param {Roo.bootstrap.DateSplitField} this
32363          * @param {Object} years
32364          */
32365         "years" : true,
32366         /**
32367          * @event days
32368          * getting the data of days
32369          * @param {Roo.bootstrap.DateSplitField} this
32370          * @param {Object} days
32371          */
32372         "days" : true,
32373         /**
32374          * @event invalid
32375          * Fires after the field has been marked as invalid.
32376          * @param {Roo.form.Field} this
32377          * @param {String} msg The validation message
32378          */
32379         invalid : true,
32380        /**
32381          * @event valid
32382          * Fires after the field has been validated with no errors.
32383          * @param {Roo.form.Field} this
32384          */
32385         valid : true
32386     });
32387 };
32388
32389 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32390     
32391     fieldLabel : '',
32392     labelAlign : 'top',
32393     labelWidth : 3,
32394     dayAllowBlank : false,
32395     monthAllowBlank : false,
32396     yearAllowBlank : false,
32397     dayPlaceholder : '',
32398     monthPlaceholder : '',
32399     yearPlaceholder : '',
32400     dayFormat : 'd',
32401     monthFormat : 'm',
32402     yearFormat : 'Y',
32403     isFormField : true,
32404     labellg : 0,
32405     labelmd : 0,
32406     labelsm : 0,
32407     labelxs : 0,
32408     
32409     getAutoCreate : function()
32410     {
32411         var cfg = {
32412             tag : 'div',
32413             cls : 'row roo-date-split-field-group',
32414             cn : [
32415                 {
32416                     tag : 'input',
32417                     type : 'hidden',
32418                     cls : 'form-hidden-field roo-date-split-field-group-value',
32419                     name : this.name
32420                 }
32421             ]
32422         };
32423         
32424         var labelCls = 'col-md-12';
32425         var contentCls = 'col-md-4';
32426         
32427         if(this.fieldLabel){
32428             
32429             var label = {
32430                 tag : 'div',
32431                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32432                 cn : [
32433                     {
32434                         tag : 'label',
32435                         html : this.fieldLabel
32436                     }
32437                 ]
32438             };
32439             
32440             if(this.labelAlign == 'left'){
32441             
32442                 if(this.labelWidth > 12){
32443                     label.style = "width: " + this.labelWidth + 'px';
32444                 }
32445
32446                 if(this.labelWidth < 13 && this.labelmd == 0){
32447                     this.labelmd = this.labelWidth;
32448                 }
32449
32450                 if(this.labellg > 0){
32451                     labelCls = ' col-lg-' + this.labellg;
32452                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32453                 }
32454
32455                 if(this.labelmd > 0){
32456                     labelCls = ' col-md-' + this.labelmd;
32457                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32458                 }
32459
32460                 if(this.labelsm > 0){
32461                     labelCls = ' col-sm-' + this.labelsm;
32462                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32463                 }
32464
32465                 if(this.labelxs > 0){
32466                     labelCls = ' col-xs-' + this.labelxs;
32467                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32468                 }
32469             }
32470             
32471             label.cls += ' ' + labelCls;
32472             
32473             cfg.cn.push(label);
32474         }
32475         
32476         Roo.each(['day', 'month', 'year'], function(t){
32477             cfg.cn.push({
32478                 tag : 'div',
32479                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32480             });
32481         }, this);
32482         
32483         return cfg;
32484     },
32485     
32486     inputEl: function ()
32487     {
32488         return this.el.select('.roo-date-split-field-group-value', true).first();
32489     },
32490     
32491     onRender : function(ct, position) 
32492     {
32493         var _this = this;
32494         
32495         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32496         
32497         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32498         
32499         this.dayField = new Roo.bootstrap.ComboBox({
32500             allowBlank : this.dayAllowBlank,
32501             alwaysQuery : true,
32502             displayField : 'value',
32503             editable : false,
32504             fieldLabel : '',
32505             forceSelection : true,
32506             mode : 'local',
32507             placeholder : this.dayPlaceholder,
32508             selectOnFocus : true,
32509             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32510             triggerAction : 'all',
32511             typeAhead : true,
32512             valueField : 'value',
32513             store : new Roo.data.SimpleStore({
32514                 data : (function() {    
32515                     var days = [];
32516                     _this.fireEvent('days', _this, days);
32517                     return days;
32518                 })(),
32519                 fields : [ 'value' ]
32520             }),
32521             listeners : {
32522                 select : function (_self, record, index)
32523                 {
32524                     _this.setValue(_this.getValue());
32525                 }
32526             }
32527         });
32528
32529         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32530         
32531         this.monthField = new Roo.bootstrap.MonthField({
32532             after : '<i class=\"fa fa-calendar\"></i>',
32533             allowBlank : this.monthAllowBlank,
32534             placeholder : this.monthPlaceholder,
32535             readOnly : true,
32536             listeners : {
32537                 render : function (_self)
32538                 {
32539                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
32540                         e.preventDefault();
32541                         _self.focus();
32542                     });
32543                 },
32544                 select : function (_self, oldvalue, newvalue)
32545                 {
32546                     _this.setValue(_this.getValue());
32547                 }
32548             }
32549         });
32550         
32551         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32552         
32553         this.yearField = new Roo.bootstrap.ComboBox({
32554             allowBlank : this.yearAllowBlank,
32555             alwaysQuery : true,
32556             displayField : 'value',
32557             editable : false,
32558             fieldLabel : '',
32559             forceSelection : true,
32560             mode : 'local',
32561             placeholder : this.yearPlaceholder,
32562             selectOnFocus : true,
32563             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32564             triggerAction : 'all',
32565             typeAhead : true,
32566             valueField : 'value',
32567             store : new Roo.data.SimpleStore({
32568                 data : (function() {
32569                     var years = [];
32570                     _this.fireEvent('years', _this, years);
32571                     return years;
32572                 })(),
32573                 fields : [ 'value' ]
32574             }),
32575             listeners : {
32576                 select : function (_self, record, index)
32577                 {
32578                     _this.setValue(_this.getValue());
32579                 }
32580             }
32581         });
32582
32583         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32584     },
32585     
32586     setValue : function(v, format)
32587     {
32588         this.inputEl.dom.value = v;
32589         
32590         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32591         
32592         var d = Date.parseDate(v, f);
32593         
32594         if(!d){
32595             this.validate();
32596             return;
32597         }
32598         
32599         this.setDay(d.format(this.dayFormat));
32600         this.setMonth(d.format(this.monthFormat));
32601         this.setYear(d.format(this.yearFormat));
32602         
32603         this.validate();
32604         
32605         return;
32606     },
32607     
32608     setDay : function(v)
32609     {
32610         this.dayField.setValue(v);
32611         this.inputEl.dom.value = this.getValue();
32612         this.validate();
32613         return;
32614     },
32615     
32616     setMonth : function(v)
32617     {
32618         this.monthField.setValue(v, true);
32619         this.inputEl.dom.value = this.getValue();
32620         this.validate();
32621         return;
32622     },
32623     
32624     setYear : function(v)
32625     {
32626         this.yearField.setValue(v);
32627         this.inputEl.dom.value = this.getValue();
32628         this.validate();
32629         return;
32630     },
32631     
32632     getDay : function()
32633     {
32634         return this.dayField.getValue();
32635     },
32636     
32637     getMonth : function()
32638     {
32639         return this.monthField.getValue();
32640     },
32641     
32642     getYear : function()
32643     {
32644         return this.yearField.getValue();
32645     },
32646     
32647     getValue : function()
32648     {
32649         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32650         
32651         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32652         
32653         return date;
32654     },
32655     
32656     reset : function()
32657     {
32658         this.setDay('');
32659         this.setMonth('');
32660         this.setYear('');
32661         this.inputEl.dom.value = '';
32662         this.validate();
32663         return;
32664     },
32665     
32666     validate : function()
32667     {
32668         var d = this.dayField.validate();
32669         var m = this.monthField.validate();
32670         var y = this.yearField.validate();
32671         
32672         var valid = true;
32673         
32674         if(
32675                 (!this.dayAllowBlank && !d) ||
32676                 (!this.monthAllowBlank && !m) ||
32677                 (!this.yearAllowBlank && !y)
32678         ){
32679             valid = false;
32680         }
32681         
32682         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32683             return valid;
32684         }
32685         
32686         if(valid){
32687             this.markValid();
32688             return valid;
32689         }
32690         
32691         this.markInvalid();
32692         
32693         return valid;
32694     },
32695     
32696     markValid : function()
32697     {
32698         
32699         var label = this.el.select('label', true).first();
32700         var icon = this.el.select('i.fa-star', true).first();
32701
32702         if(label && icon){
32703             icon.remove();
32704         }
32705         
32706         this.fireEvent('valid', this);
32707     },
32708     
32709      /**
32710      * Mark this field as invalid
32711      * @param {String} msg The validation message
32712      */
32713     markInvalid : function(msg)
32714     {
32715         
32716         var label = this.el.select('label', true).first();
32717         var icon = this.el.select('i.fa-star', true).first();
32718
32719         if(label && !icon){
32720             this.el.select('.roo-date-split-field-label', true).createChild({
32721                 tag : 'i',
32722                 cls : 'text-danger fa fa-lg fa-star',
32723                 tooltip : 'This field is required',
32724                 style : 'margin-right:5px;'
32725             }, label, true);
32726         }
32727         
32728         this.fireEvent('invalid', this, msg);
32729     },
32730     
32731     clearInvalid : function()
32732     {
32733         var label = this.el.select('label', true).first();
32734         var icon = this.el.select('i.fa-star', true).first();
32735
32736         if(label && icon){
32737             icon.remove();
32738         }
32739         
32740         this.fireEvent('valid', this);
32741     },
32742     
32743     getName: function()
32744     {
32745         return this.name;
32746     }
32747     
32748 });
32749
32750  /**
32751  *
32752  * This is based on 
32753  * http://masonry.desandro.com
32754  *
32755  * The idea is to render all the bricks based on vertical width...
32756  *
32757  * The original code extends 'outlayer' - we might need to use that....
32758  * 
32759  */
32760
32761
32762 /**
32763  * @class Roo.bootstrap.LayoutMasonry
32764  * @extends Roo.bootstrap.Component
32765  * Bootstrap Layout Masonry class
32766  * 
32767  * @constructor
32768  * Create a new Element
32769  * @param {Object} config The config object
32770  */
32771
32772 Roo.bootstrap.LayoutMasonry = function(config){
32773     
32774     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32775     
32776     this.bricks = [];
32777     
32778     Roo.bootstrap.LayoutMasonry.register(this);
32779     
32780     this.addEvents({
32781         // raw events
32782         /**
32783          * @event layout
32784          * Fire after layout the items
32785          * @param {Roo.bootstrap.LayoutMasonry} this
32786          * @param {Roo.EventObject} e
32787          */
32788         "layout" : true
32789     });
32790     
32791 };
32792
32793 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
32794     
32795     /**
32796      * @cfg {Boolean} isLayoutInstant = no animation?
32797      */   
32798     isLayoutInstant : false, // needed?
32799    
32800     /**
32801      * @cfg {Number} boxWidth  width of the columns
32802      */   
32803     boxWidth : 450,
32804     
32805       /**
32806      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
32807      */   
32808     boxHeight : 0,
32809     
32810     /**
32811      * @cfg {Number} padWidth padding below box..
32812      */   
32813     padWidth : 10, 
32814     
32815     /**
32816      * @cfg {Number} gutter gutter width..
32817      */   
32818     gutter : 10,
32819     
32820      /**
32821      * @cfg {Number} maxCols maximum number of columns
32822      */   
32823     
32824     maxCols: 0,
32825     
32826     /**
32827      * @cfg {Boolean} isAutoInitial defalut true
32828      */   
32829     isAutoInitial : true, 
32830     
32831     containerWidth: 0,
32832     
32833     /**
32834      * @cfg {Boolean} isHorizontal defalut false
32835      */   
32836     isHorizontal : false, 
32837
32838     currentSize : null,
32839     
32840     tag: 'div',
32841     
32842     cls: '',
32843     
32844     bricks: null, //CompositeElement
32845     
32846     cols : 1,
32847     
32848     _isLayoutInited : false,
32849     
32850 //    isAlternative : false, // only use for vertical layout...
32851     
32852     /**
32853      * @cfg {Number} alternativePadWidth padding below box..
32854      */   
32855     alternativePadWidth : 50,
32856     
32857     selectedBrick : [],
32858     
32859     getAutoCreate : function(){
32860         
32861         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
32862         
32863         var cfg = {
32864             tag: this.tag,
32865             cls: 'blog-masonary-wrapper ' + this.cls,
32866             cn : {
32867                 cls : 'mas-boxes masonary'
32868             }
32869         };
32870         
32871         return cfg;
32872     },
32873     
32874     getChildContainer: function( )
32875     {
32876         if (this.boxesEl) {
32877             return this.boxesEl;
32878         }
32879         
32880         this.boxesEl = this.el.select('.mas-boxes').first();
32881         
32882         return this.boxesEl;
32883     },
32884     
32885     
32886     initEvents : function()
32887     {
32888         var _this = this;
32889         
32890         if(this.isAutoInitial){
32891             Roo.log('hook children rendered');
32892             this.on('childrenrendered', function() {
32893                 Roo.log('children rendered');
32894                 _this.initial();
32895             } ,this);
32896         }
32897     },
32898     
32899     initial : function()
32900     {
32901         this.selectedBrick = [];
32902         
32903         this.currentSize = this.el.getBox(true);
32904         
32905         Roo.EventManager.onWindowResize(this.resize, this); 
32906
32907         if(!this.isAutoInitial){
32908             this.layout();
32909             return;
32910         }
32911         
32912         this.layout();
32913         
32914         return;
32915         //this.layout.defer(500,this);
32916         
32917     },
32918     
32919     resize : function()
32920     {
32921         var cs = this.el.getBox(true);
32922         
32923         if (
32924                 this.currentSize.width == cs.width && 
32925                 this.currentSize.x == cs.x && 
32926                 this.currentSize.height == cs.height && 
32927                 this.currentSize.y == cs.y 
32928         ) {
32929             Roo.log("no change in with or X or Y");
32930             return;
32931         }
32932         
32933         this.currentSize = cs;
32934         
32935         this.layout();
32936         
32937     },
32938     
32939     layout : function()
32940     {   
32941         this._resetLayout();
32942         
32943         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32944         
32945         this.layoutItems( isInstant );
32946       
32947         this._isLayoutInited = true;
32948         
32949         this.fireEvent('layout', this);
32950         
32951     },
32952     
32953     _resetLayout : function()
32954     {
32955         if(this.isHorizontal){
32956             this.horizontalMeasureColumns();
32957             return;
32958         }
32959         
32960         this.verticalMeasureColumns();
32961         
32962     },
32963     
32964     verticalMeasureColumns : function()
32965     {
32966         this.getContainerWidth();
32967         
32968 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
32969 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
32970 //            return;
32971 //        }
32972         
32973         var boxWidth = this.boxWidth + this.padWidth;
32974         
32975         if(this.containerWidth < this.boxWidth){
32976             boxWidth = this.containerWidth
32977         }
32978         
32979         var containerWidth = this.containerWidth;
32980         
32981         var cols = Math.floor(containerWidth / boxWidth);
32982         
32983         this.cols = Math.max( cols, 1 );
32984         
32985         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32986         
32987         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
32988         
32989         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
32990         
32991         this.colWidth = boxWidth + avail - this.padWidth;
32992         
32993         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
32994         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
32995     },
32996     
32997     horizontalMeasureColumns : function()
32998     {
32999         this.getContainerWidth();
33000         
33001         var boxWidth = this.boxWidth;
33002         
33003         if(this.containerWidth < boxWidth){
33004             boxWidth = this.containerWidth;
33005         }
33006         
33007         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33008         
33009         this.el.setHeight(boxWidth);
33010         
33011     },
33012     
33013     getContainerWidth : function()
33014     {
33015         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33016     },
33017     
33018     layoutItems : function( isInstant )
33019     {
33020         Roo.log(this.bricks);
33021         
33022         var items = Roo.apply([], this.bricks);
33023         
33024         if(this.isHorizontal){
33025             this._horizontalLayoutItems( items , isInstant );
33026             return;
33027         }
33028         
33029 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33030 //            this._verticalAlternativeLayoutItems( items , isInstant );
33031 //            return;
33032 //        }
33033         
33034         this._verticalLayoutItems( items , isInstant );
33035         
33036     },
33037     
33038     _verticalLayoutItems : function ( items , isInstant)
33039     {
33040         if ( !items || !items.length ) {
33041             return;
33042         }
33043         
33044         var standard = [
33045             ['xs', 'xs', 'xs', 'tall'],
33046             ['xs', 'xs', 'tall'],
33047             ['xs', 'xs', 'sm'],
33048             ['xs', 'xs', 'xs'],
33049             ['xs', 'tall'],
33050             ['xs', 'sm'],
33051             ['xs', 'xs'],
33052             ['xs'],
33053             
33054             ['sm', 'xs', 'xs'],
33055             ['sm', 'xs'],
33056             ['sm'],
33057             
33058             ['tall', 'xs', 'xs', 'xs'],
33059             ['tall', 'xs', 'xs'],
33060             ['tall', 'xs'],
33061             ['tall']
33062             
33063         ];
33064         
33065         var queue = [];
33066         
33067         var boxes = [];
33068         
33069         var box = [];
33070         
33071         Roo.each(items, function(item, k){
33072             
33073             switch (item.size) {
33074                 // these layouts take up a full box,
33075                 case 'md' :
33076                 case 'md-left' :
33077                 case 'md-right' :
33078                 case 'wide' :
33079                     
33080                     if(box.length){
33081                         boxes.push(box);
33082                         box = [];
33083                     }
33084                     
33085                     boxes.push([item]);
33086                     
33087                     break;
33088                     
33089                 case 'xs' :
33090                 case 'sm' :
33091                 case 'tall' :
33092                     
33093                     box.push(item);
33094                     
33095                     break;
33096                 default :
33097                     break;
33098                     
33099             }
33100             
33101         }, this);
33102         
33103         if(box.length){
33104             boxes.push(box);
33105             box = [];
33106         }
33107         
33108         var filterPattern = function(box, length)
33109         {
33110             if(!box.length){
33111                 return;
33112             }
33113             
33114             var match = false;
33115             
33116             var pattern = box.slice(0, length);
33117             
33118             var format = [];
33119             
33120             Roo.each(pattern, function(i){
33121                 format.push(i.size);
33122             }, this);
33123             
33124             Roo.each(standard, function(s){
33125                 
33126                 if(String(s) != String(format)){
33127                     return;
33128                 }
33129                 
33130                 match = true;
33131                 return false;
33132                 
33133             }, this);
33134             
33135             if(!match && length == 1){
33136                 return;
33137             }
33138             
33139             if(!match){
33140                 filterPattern(box, length - 1);
33141                 return;
33142             }
33143                 
33144             queue.push(pattern);
33145
33146             box = box.slice(length, box.length);
33147
33148             filterPattern(box, 4);
33149
33150             return;
33151             
33152         }
33153         
33154         Roo.each(boxes, function(box, k){
33155             
33156             if(!box.length){
33157                 return;
33158             }
33159             
33160             if(box.length == 1){
33161                 queue.push(box);
33162                 return;
33163             }
33164             
33165             filterPattern(box, 4);
33166             
33167         }, this);
33168         
33169         this._processVerticalLayoutQueue( queue, isInstant );
33170         
33171     },
33172     
33173 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33174 //    {
33175 //        if ( !items || !items.length ) {
33176 //            return;
33177 //        }
33178 //
33179 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33180 //        
33181 //    },
33182     
33183     _horizontalLayoutItems : function ( items , isInstant)
33184     {
33185         if ( !items || !items.length || items.length < 3) {
33186             return;
33187         }
33188         
33189         items.reverse();
33190         
33191         var eItems = items.slice(0, 3);
33192         
33193         items = items.slice(3, items.length);
33194         
33195         var standard = [
33196             ['xs', 'xs', 'xs', 'wide'],
33197             ['xs', 'xs', 'wide'],
33198             ['xs', 'xs', 'sm'],
33199             ['xs', 'xs', 'xs'],
33200             ['xs', 'wide'],
33201             ['xs', 'sm'],
33202             ['xs', 'xs'],
33203             ['xs'],
33204             
33205             ['sm', 'xs', 'xs'],
33206             ['sm', 'xs'],
33207             ['sm'],
33208             
33209             ['wide', 'xs', 'xs', 'xs'],
33210             ['wide', 'xs', 'xs'],
33211             ['wide', 'xs'],
33212             ['wide'],
33213             
33214             ['wide-thin']
33215         ];
33216         
33217         var queue = [];
33218         
33219         var boxes = [];
33220         
33221         var box = [];
33222         
33223         Roo.each(items, function(item, k){
33224             
33225             switch (item.size) {
33226                 case 'md' :
33227                 case 'md-left' :
33228                 case 'md-right' :
33229                 case 'tall' :
33230                     
33231                     if(box.length){
33232                         boxes.push(box);
33233                         box = [];
33234                     }
33235                     
33236                     boxes.push([item]);
33237                     
33238                     break;
33239                     
33240                 case 'xs' :
33241                 case 'sm' :
33242                 case 'wide' :
33243                 case 'wide-thin' :
33244                     
33245                     box.push(item);
33246                     
33247                     break;
33248                 default :
33249                     break;
33250                     
33251             }
33252             
33253         }, this);
33254         
33255         if(box.length){
33256             boxes.push(box);
33257             box = [];
33258         }
33259         
33260         var filterPattern = function(box, length)
33261         {
33262             if(!box.length){
33263                 return;
33264             }
33265             
33266             var match = false;
33267             
33268             var pattern = box.slice(0, length);
33269             
33270             var format = [];
33271             
33272             Roo.each(pattern, function(i){
33273                 format.push(i.size);
33274             }, this);
33275             
33276             Roo.each(standard, function(s){
33277                 
33278                 if(String(s) != String(format)){
33279                     return;
33280                 }
33281                 
33282                 match = true;
33283                 return false;
33284                 
33285             }, this);
33286             
33287             if(!match && length == 1){
33288                 return;
33289             }
33290             
33291             if(!match){
33292                 filterPattern(box, length - 1);
33293                 return;
33294             }
33295                 
33296             queue.push(pattern);
33297
33298             box = box.slice(length, box.length);
33299
33300             filterPattern(box, 4);
33301
33302             return;
33303             
33304         }
33305         
33306         Roo.each(boxes, function(box, k){
33307             
33308             if(!box.length){
33309                 return;
33310             }
33311             
33312             if(box.length == 1){
33313                 queue.push(box);
33314                 return;
33315             }
33316             
33317             filterPattern(box, 4);
33318             
33319         }, this);
33320         
33321         
33322         var prune = [];
33323         
33324         var pos = this.el.getBox(true);
33325         
33326         var minX = pos.x;
33327         
33328         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33329         
33330         var hit_end = false;
33331         
33332         Roo.each(queue, function(box){
33333             
33334             if(hit_end){
33335                 
33336                 Roo.each(box, function(b){
33337                 
33338                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33339                     b.el.hide();
33340
33341                 }, this);
33342
33343                 return;
33344             }
33345             
33346             var mx = 0;
33347             
33348             Roo.each(box, function(b){
33349                 
33350                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33351                 b.el.show();
33352
33353                 mx = Math.max(mx, b.x);
33354                 
33355             }, this);
33356             
33357             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33358             
33359             if(maxX < minX){
33360                 
33361                 Roo.each(box, function(b){
33362                 
33363                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33364                     b.el.hide();
33365                     
33366                 }, this);
33367                 
33368                 hit_end = true;
33369                 
33370                 return;
33371             }
33372             
33373             prune.push(box);
33374             
33375         }, this);
33376         
33377         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33378     },
33379     
33380     /** Sets position of item in DOM
33381     * @param {Element} item
33382     * @param {Number} x - horizontal position
33383     * @param {Number} y - vertical position
33384     * @param {Boolean} isInstant - disables transitions
33385     */
33386     _processVerticalLayoutQueue : function( queue, isInstant )
33387     {
33388         var pos = this.el.getBox(true);
33389         var x = pos.x;
33390         var y = pos.y;
33391         var maxY = [];
33392         
33393         for (var i = 0; i < this.cols; i++){
33394             maxY[i] = pos.y;
33395         }
33396         
33397         Roo.each(queue, function(box, k){
33398             
33399             var col = k % this.cols;
33400             
33401             Roo.each(box, function(b,kk){
33402                 
33403                 b.el.position('absolute');
33404                 
33405                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33406                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33407                 
33408                 if(b.size == 'md-left' || b.size == 'md-right'){
33409                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33410                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33411                 }
33412                 
33413                 b.el.setWidth(width);
33414                 b.el.setHeight(height);
33415                 // iframe?
33416                 b.el.select('iframe',true).setSize(width,height);
33417                 
33418             }, this);
33419             
33420             for (var i = 0; i < this.cols; i++){
33421                 
33422                 if(maxY[i] < maxY[col]){
33423                     col = i;
33424                     continue;
33425                 }
33426                 
33427                 col = Math.min(col, i);
33428                 
33429             }
33430             
33431             x = pos.x + col * (this.colWidth + this.padWidth);
33432             
33433             y = maxY[col];
33434             
33435             var positions = [];
33436             
33437             switch (box.length){
33438                 case 1 :
33439                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33440                     break;
33441                 case 2 :
33442                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33443                     break;
33444                 case 3 :
33445                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33446                     break;
33447                 case 4 :
33448                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33449                     break;
33450                 default :
33451                     break;
33452             }
33453             
33454             Roo.each(box, function(b,kk){
33455                 
33456                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33457                 
33458                 var sz = b.el.getSize();
33459                 
33460                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33461                 
33462             }, this);
33463             
33464         }, this);
33465         
33466         var mY = 0;
33467         
33468         for (var i = 0; i < this.cols; i++){
33469             mY = Math.max(mY, maxY[i]);
33470         }
33471         
33472         this.el.setHeight(mY - pos.y);
33473         
33474     },
33475     
33476 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33477 //    {
33478 //        var pos = this.el.getBox(true);
33479 //        var x = pos.x;
33480 //        var y = pos.y;
33481 //        var maxX = pos.right;
33482 //        
33483 //        var maxHeight = 0;
33484 //        
33485 //        Roo.each(items, function(item, k){
33486 //            
33487 //            var c = k % 2;
33488 //            
33489 //            item.el.position('absolute');
33490 //                
33491 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33492 //
33493 //            item.el.setWidth(width);
33494 //
33495 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33496 //
33497 //            item.el.setHeight(height);
33498 //            
33499 //            if(c == 0){
33500 //                item.el.setXY([x, y], isInstant ? false : true);
33501 //            } else {
33502 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33503 //            }
33504 //            
33505 //            y = y + height + this.alternativePadWidth;
33506 //            
33507 //            maxHeight = maxHeight + height + this.alternativePadWidth;
33508 //            
33509 //        }, this);
33510 //        
33511 //        this.el.setHeight(maxHeight);
33512 //        
33513 //    },
33514     
33515     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33516     {
33517         var pos = this.el.getBox(true);
33518         
33519         var minX = pos.x;
33520         var minY = pos.y;
33521         
33522         var maxX = pos.right;
33523         
33524         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33525         
33526         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33527         
33528         Roo.each(queue, function(box, k){
33529             
33530             Roo.each(box, function(b, kk){
33531                 
33532                 b.el.position('absolute');
33533                 
33534                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33535                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33536                 
33537                 if(b.size == 'md-left' || b.size == 'md-right'){
33538                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33539                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33540                 }
33541                 
33542                 b.el.setWidth(width);
33543                 b.el.setHeight(height);
33544                 
33545             }, this);
33546             
33547             if(!box.length){
33548                 return;
33549             }
33550             
33551             var positions = [];
33552             
33553             switch (box.length){
33554                 case 1 :
33555                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33556                     break;
33557                 case 2 :
33558                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33559                     break;
33560                 case 3 :
33561                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33562                     break;
33563                 case 4 :
33564                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33565                     break;
33566                 default :
33567                     break;
33568             }
33569             
33570             Roo.each(box, function(b,kk){
33571                 
33572                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33573                 
33574                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33575                 
33576             }, this);
33577             
33578         }, this);
33579         
33580     },
33581     
33582     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33583     {
33584         Roo.each(eItems, function(b,k){
33585             
33586             b.size = (k == 0) ? 'sm' : 'xs';
33587             b.x = (k == 0) ? 2 : 1;
33588             b.y = (k == 0) ? 2 : 1;
33589             
33590             b.el.position('absolute');
33591             
33592             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33593                 
33594             b.el.setWidth(width);
33595             
33596             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33597             
33598             b.el.setHeight(height);
33599             
33600         }, this);
33601
33602         var positions = [];
33603         
33604         positions.push({
33605             x : maxX - this.unitWidth * 2 - this.gutter,
33606             y : minY
33607         });
33608         
33609         positions.push({
33610             x : maxX - this.unitWidth,
33611             y : minY + (this.unitWidth + this.gutter) * 2
33612         });
33613         
33614         positions.push({
33615             x : maxX - this.unitWidth * 3 - this.gutter * 2,
33616             y : minY
33617         });
33618         
33619         Roo.each(eItems, function(b,k){
33620             
33621             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33622
33623         }, this);
33624         
33625     },
33626     
33627     getVerticalOneBoxColPositions : function(x, y, box)
33628     {
33629         var pos = [];
33630         
33631         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33632         
33633         if(box[0].size == 'md-left'){
33634             rand = 0;
33635         }
33636         
33637         if(box[0].size == 'md-right'){
33638             rand = 1;
33639         }
33640         
33641         pos.push({
33642             x : x + (this.unitWidth + this.gutter) * rand,
33643             y : y
33644         });
33645         
33646         return pos;
33647     },
33648     
33649     getVerticalTwoBoxColPositions : function(x, y, box)
33650     {
33651         var pos = [];
33652         
33653         if(box[0].size == 'xs'){
33654             
33655             pos.push({
33656                 x : x,
33657                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33658             });
33659
33660             pos.push({
33661                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33662                 y : y
33663             });
33664             
33665             return pos;
33666             
33667         }
33668         
33669         pos.push({
33670             x : x,
33671             y : y
33672         });
33673
33674         pos.push({
33675             x : x + (this.unitWidth + this.gutter) * 2,
33676             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33677         });
33678         
33679         return pos;
33680         
33681     },
33682     
33683     getVerticalThreeBoxColPositions : function(x, y, box)
33684     {
33685         var pos = [];
33686         
33687         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33688             
33689             pos.push({
33690                 x : x,
33691                 y : y
33692             });
33693
33694             pos.push({
33695                 x : x + (this.unitWidth + this.gutter) * 1,
33696                 y : y
33697             });
33698             
33699             pos.push({
33700                 x : x + (this.unitWidth + this.gutter) * 2,
33701                 y : y
33702             });
33703             
33704             return pos;
33705             
33706         }
33707         
33708         if(box[0].size == 'xs' && box[1].size == 'xs'){
33709             
33710             pos.push({
33711                 x : x,
33712                 y : y
33713             });
33714
33715             pos.push({
33716                 x : x,
33717                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33718             });
33719             
33720             pos.push({
33721                 x : x + (this.unitWidth + this.gutter) * 1,
33722                 y : y
33723             });
33724             
33725             return pos;
33726             
33727         }
33728         
33729         pos.push({
33730             x : x,
33731             y : y
33732         });
33733
33734         pos.push({
33735             x : x + (this.unitWidth + this.gutter) * 2,
33736             y : y
33737         });
33738
33739         pos.push({
33740             x : x + (this.unitWidth + this.gutter) * 2,
33741             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33742         });
33743             
33744         return pos;
33745         
33746     },
33747     
33748     getVerticalFourBoxColPositions : function(x, y, box)
33749     {
33750         var pos = [];
33751         
33752         if(box[0].size == 'xs'){
33753             
33754             pos.push({
33755                 x : x,
33756                 y : y
33757             });
33758
33759             pos.push({
33760                 x : x,
33761                 y : y + (this.unitHeight + this.gutter) * 1
33762             });
33763             
33764             pos.push({
33765                 x : x,
33766                 y : y + (this.unitHeight + this.gutter) * 2
33767             });
33768             
33769             pos.push({
33770                 x : x + (this.unitWidth + this.gutter) * 1,
33771                 y : y
33772             });
33773             
33774             return pos;
33775             
33776         }
33777         
33778         pos.push({
33779             x : x,
33780             y : y
33781         });
33782
33783         pos.push({
33784             x : x + (this.unitWidth + this.gutter) * 2,
33785             y : y
33786         });
33787
33788         pos.push({
33789             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
33790             y : y + (this.unitHeight + this.gutter) * 1
33791         });
33792
33793         pos.push({
33794             x : x + (this.unitWidth + this.gutter) * 2,
33795             y : y + (this.unitWidth + this.gutter) * 2
33796         });
33797
33798         return pos;
33799         
33800     },
33801     
33802     getHorizontalOneBoxColPositions : function(maxX, minY, box)
33803     {
33804         var pos = [];
33805         
33806         if(box[0].size == 'md-left'){
33807             pos.push({
33808                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33809                 y : minY
33810             });
33811             
33812             return pos;
33813         }
33814         
33815         if(box[0].size == 'md-right'){
33816             pos.push({
33817                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33818                 y : minY + (this.unitWidth + this.gutter) * 1
33819             });
33820             
33821             return pos;
33822         }
33823         
33824         var rand = Math.floor(Math.random() * (4 - box[0].y));
33825         
33826         pos.push({
33827             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33828             y : minY + (this.unitWidth + this.gutter) * rand
33829         });
33830         
33831         return pos;
33832         
33833     },
33834     
33835     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
33836     {
33837         var pos = [];
33838         
33839         if(box[0].size == 'xs'){
33840             
33841             pos.push({
33842                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33843                 y : minY
33844             });
33845
33846             pos.push({
33847                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33848                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
33849             });
33850             
33851             return pos;
33852             
33853         }
33854         
33855         pos.push({
33856             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33857             y : minY
33858         });
33859
33860         pos.push({
33861             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33862             y : minY + (this.unitWidth + this.gutter) * 2
33863         });
33864         
33865         return pos;
33866         
33867     },
33868     
33869     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
33870     {
33871         var pos = [];
33872         
33873         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33874             
33875             pos.push({
33876                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33877                 y : minY
33878             });
33879
33880             pos.push({
33881                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33882                 y : minY + (this.unitWidth + this.gutter) * 1
33883             });
33884             
33885             pos.push({
33886                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33887                 y : minY + (this.unitWidth + this.gutter) * 2
33888             });
33889             
33890             return pos;
33891             
33892         }
33893         
33894         if(box[0].size == 'xs' && box[1].size == 'xs'){
33895             
33896             pos.push({
33897                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33898                 y : minY
33899             });
33900
33901             pos.push({
33902                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33903                 y : minY
33904             });
33905             
33906             pos.push({
33907                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33908                 y : minY + (this.unitWidth + this.gutter) * 1
33909             });
33910             
33911             return pos;
33912             
33913         }
33914         
33915         pos.push({
33916             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33917             y : minY
33918         });
33919
33920         pos.push({
33921             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33922             y : minY + (this.unitWidth + this.gutter) * 2
33923         });
33924
33925         pos.push({
33926             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33927             y : minY + (this.unitWidth + this.gutter) * 2
33928         });
33929             
33930         return pos;
33931         
33932     },
33933     
33934     getHorizontalFourBoxColPositions : function(maxX, minY, box)
33935     {
33936         var pos = [];
33937         
33938         if(box[0].size == 'xs'){
33939             
33940             pos.push({
33941                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33942                 y : minY
33943             });
33944
33945             pos.push({
33946                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33947                 y : minY
33948             });
33949             
33950             pos.push({
33951                 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),
33952                 y : minY
33953             });
33954             
33955             pos.push({
33956                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
33957                 y : minY + (this.unitWidth + this.gutter) * 1
33958             });
33959             
33960             return pos;
33961             
33962         }
33963         
33964         pos.push({
33965             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33966             y : minY
33967         });
33968         
33969         pos.push({
33970             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33971             y : minY + (this.unitWidth + this.gutter) * 2
33972         });
33973         
33974         pos.push({
33975             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33976             y : minY + (this.unitWidth + this.gutter) * 2
33977         });
33978         
33979         pos.push({
33980             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),
33981             y : minY + (this.unitWidth + this.gutter) * 2
33982         });
33983
33984         return pos;
33985         
33986     },
33987     
33988     /**
33989     * remove a Masonry Brick
33990     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
33991     */
33992     removeBrick : function(brick_id)
33993     {
33994         if (!brick_id) {
33995             return;
33996         }
33997         
33998         for (var i = 0; i<this.bricks.length; i++) {
33999             if (this.bricks[i].id == brick_id) {
34000                 this.bricks.splice(i,1);
34001                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34002                 this.initial();
34003             }
34004         }
34005     },
34006     
34007     /**
34008     * adds a Masonry Brick
34009     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34010     */
34011     addBrick : function(cfg)
34012     {
34013         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34014         //this.register(cn);
34015         cn.parentId = this.id;
34016         cn.render(this.el);
34017         return cn;
34018     },
34019     
34020     /**
34021     * register a Masonry Brick
34022     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34023     */
34024     
34025     register : function(brick)
34026     {
34027         this.bricks.push(brick);
34028         brick.masonryId = this.id;
34029     },
34030     
34031     /**
34032     * clear all the Masonry Brick
34033     */
34034     clearAll : function()
34035     {
34036         this.bricks = [];
34037         //this.getChildContainer().dom.innerHTML = "";
34038         this.el.dom.innerHTML = '';
34039     },
34040     
34041     getSelected : function()
34042     {
34043         if (!this.selectedBrick) {
34044             return false;
34045         }
34046         
34047         return this.selectedBrick;
34048     }
34049 });
34050
34051 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34052     
34053     groups: {},
34054      /**
34055     * register a Masonry Layout
34056     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34057     */
34058     
34059     register : function(layout)
34060     {
34061         this.groups[layout.id] = layout;
34062     },
34063     /**
34064     * fetch a  Masonry Layout based on the masonry layout ID
34065     * @param {string} the masonry layout to add
34066     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34067     */
34068     
34069     get: function(layout_id) {
34070         if (typeof(this.groups[layout_id]) == 'undefined') {
34071             return false;
34072         }
34073         return this.groups[layout_id] ;
34074     }
34075     
34076     
34077     
34078 });
34079
34080  
34081
34082  /**
34083  *
34084  * This is based on 
34085  * http://masonry.desandro.com
34086  *
34087  * The idea is to render all the bricks based on vertical width...
34088  *
34089  * The original code extends 'outlayer' - we might need to use that....
34090  * 
34091  */
34092
34093
34094 /**
34095  * @class Roo.bootstrap.LayoutMasonryAuto
34096  * @extends Roo.bootstrap.Component
34097  * Bootstrap Layout Masonry class
34098  * 
34099  * @constructor
34100  * Create a new Element
34101  * @param {Object} config The config object
34102  */
34103
34104 Roo.bootstrap.LayoutMasonryAuto = function(config){
34105     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34106 };
34107
34108 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34109     
34110       /**
34111      * @cfg {Boolean} isFitWidth  - resize the width..
34112      */   
34113     isFitWidth : false,  // options..
34114     /**
34115      * @cfg {Boolean} isOriginLeft = left align?
34116      */   
34117     isOriginLeft : true,
34118     /**
34119      * @cfg {Boolean} isOriginTop = top align?
34120      */   
34121     isOriginTop : false,
34122     /**
34123      * @cfg {Boolean} isLayoutInstant = no animation?
34124      */   
34125     isLayoutInstant : false, // needed?
34126     /**
34127      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34128      */   
34129     isResizingContainer : true,
34130     /**
34131      * @cfg {Number} columnWidth  width of the columns 
34132      */   
34133     
34134     columnWidth : 0,
34135     
34136     /**
34137      * @cfg {Number} maxCols maximum number of columns
34138      */   
34139     
34140     maxCols: 0,
34141     /**
34142      * @cfg {Number} padHeight padding below box..
34143      */   
34144     
34145     padHeight : 10, 
34146     
34147     /**
34148      * @cfg {Boolean} isAutoInitial defalut true
34149      */   
34150     
34151     isAutoInitial : true, 
34152     
34153     // private?
34154     gutter : 0,
34155     
34156     containerWidth: 0,
34157     initialColumnWidth : 0,
34158     currentSize : null,
34159     
34160     colYs : null, // array.
34161     maxY : 0,
34162     padWidth: 10,
34163     
34164     
34165     tag: 'div',
34166     cls: '',
34167     bricks: null, //CompositeElement
34168     cols : 0, // array?
34169     // element : null, // wrapped now this.el
34170     _isLayoutInited : null, 
34171     
34172     
34173     getAutoCreate : function(){
34174         
34175         var cfg = {
34176             tag: this.tag,
34177             cls: 'blog-masonary-wrapper ' + this.cls,
34178             cn : {
34179                 cls : 'mas-boxes masonary'
34180             }
34181         };
34182         
34183         return cfg;
34184     },
34185     
34186     getChildContainer: function( )
34187     {
34188         if (this.boxesEl) {
34189             return this.boxesEl;
34190         }
34191         
34192         this.boxesEl = this.el.select('.mas-boxes').first();
34193         
34194         return this.boxesEl;
34195     },
34196     
34197     
34198     initEvents : function()
34199     {
34200         var _this = this;
34201         
34202         if(this.isAutoInitial){
34203             Roo.log('hook children rendered');
34204             this.on('childrenrendered', function() {
34205                 Roo.log('children rendered');
34206                 _this.initial();
34207             } ,this);
34208         }
34209         
34210     },
34211     
34212     initial : function()
34213     {
34214         this.reloadItems();
34215
34216         this.currentSize = this.el.getBox(true);
34217
34218         /// was window resize... - let's see if this works..
34219         Roo.EventManager.onWindowResize(this.resize, this); 
34220
34221         if(!this.isAutoInitial){
34222             this.layout();
34223             return;
34224         }
34225         
34226         this.layout.defer(500,this);
34227     },
34228     
34229     reloadItems: function()
34230     {
34231         this.bricks = this.el.select('.masonry-brick', true);
34232         
34233         this.bricks.each(function(b) {
34234             //Roo.log(b.getSize());
34235             if (!b.attr('originalwidth')) {
34236                 b.attr('originalwidth',  b.getSize().width);
34237             }
34238             
34239         });
34240         
34241         Roo.log(this.bricks.elements.length);
34242     },
34243     
34244     resize : function()
34245     {
34246         Roo.log('resize');
34247         var cs = this.el.getBox(true);
34248         
34249         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34250             Roo.log("no change in with or X");
34251             return;
34252         }
34253         this.currentSize = cs;
34254         this.layout();
34255     },
34256     
34257     layout : function()
34258     {
34259          Roo.log('layout');
34260         this._resetLayout();
34261         //this._manageStamps();
34262       
34263         // don't animate first layout
34264         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34265         this.layoutItems( isInstant );
34266       
34267         // flag for initalized
34268         this._isLayoutInited = true;
34269     },
34270     
34271     layoutItems : function( isInstant )
34272     {
34273         //var items = this._getItemsForLayout( this.items );
34274         // original code supports filtering layout items.. we just ignore it..
34275         
34276         this._layoutItems( this.bricks , isInstant );
34277       
34278         this._postLayout();
34279     },
34280     _layoutItems : function ( items , isInstant)
34281     {
34282        //this.fireEvent( 'layout', this, items );
34283     
34284
34285         if ( !items || !items.elements.length ) {
34286           // no items, emit event with empty array
34287             return;
34288         }
34289
34290         var queue = [];
34291         items.each(function(item) {
34292             Roo.log("layout item");
34293             Roo.log(item);
34294             // get x/y object from method
34295             var position = this._getItemLayoutPosition( item );
34296             // enqueue
34297             position.item = item;
34298             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34299             queue.push( position );
34300         }, this);
34301       
34302         this._processLayoutQueue( queue );
34303     },
34304     /** Sets position of item in DOM
34305     * @param {Element} item
34306     * @param {Number} x - horizontal position
34307     * @param {Number} y - vertical position
34308     * @param {Boolean} isInstant - disables transitions
34309     */
34310     _processLayoutQueue : function( queue )
34311     {
34312         for ( var i=0, len = queue.length; i < len; i++ ) {
34313             var obj = queue[i];
34314             obj.item.position('absolute');
34315             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34316         }
34317     },
34318       
34319     
34320     /**
34321     * Any logic you want to do after each layout,
34322     * i.e. size the container
34323     */
34324     _postLayout : function()
34325     {
34326         this.resizeContainer();
34327     },
34328     
34329     resizeContainer : function()
34330     {
34331         if ( !this.isResizingContainer ) {
34332             return;
34333         }
34334         var size = this._getContainerSize();
34335         if ( size ) {
34336             this.el.setSize(size.width,size.height);
34337             this.boxesEl.setSize(size.width,size.height);
34338         }
34339     },
34340     
34341     
34342     
34343     _resetLayout : function()
34344     {
34345         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34346         this.colWidth = this.el.getWidth();
34347         //this.gutter = this.el.getWidth(); 
34348         
34349         this.measureColumns();
34350
34351         // reset column Y
34352         var i = this.cols;
34353         this.colYs = [];
34354         while (i--) {
34355             this.colYs.push( 0 );
34356         }
34357     
34358         this.maxY = 0;
34359     },
34360
34361     measureColumns : function()
34362     {
34363         this.getContainerWidth();
34364       // if columnWidth is 0, default to outerWidth of first item
34365         if ( !this.columnWidth ) {
34366             var firstItem = this.bricks.first();
34367             Roo.log(firstItem);
34368             this.columnWidth  = this.containerWidth;
34369             if (firstItem && firstItem.attr('originalwidth') ) {
34370                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34371             }
34372             // columnWidth fall back to item of first element
34373             Roo.log("set column width?");
34374                         this.initialColumnWidth = this.columnWidth  ;
34375
34376             // if first elem has no width, default to size of container
34377             
34378         }
34379         
34380         
34381         if (this.initialColumnWidth) {
34382             this.columnWidth = this.initialColumnWidth;
34383         }
34384         
34385         
34386             
34387         // column width is fixed at the top - however if container width get's smaller we should
34388         // reduce it...
34389         
34390         // this bit calcs how man columns..
34391             
34392         var columnWidth = this.columnWidth += this.gutter;
34393       
34394         // calculate columns
34395         var containerWidth = this.containerWidth + this.gutter;
34396         
34397         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34398         // fix rounding errors, typically with gutters
34399         var excess = columnWidth - containerWidth % columnWidth;
34400         
34401         
34402         // if overshoot is less than a pixel, round up, otherwise floor it
34403         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34404         cols = Math[ mathMethod ]( cols );
34405         this.cols = Math.max( cols, 1 );
34406         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34407         
34408          // padding positioning..
34409         var totalColWidth = this.cols * this.columnWidth;
34410         var padavail = this.containerWidth - totalColWidth;
34411         // so for 2 columns - we need 3 'pads'
34412         
34413         var padNeeded = (1+this.cols) * this.padWidth;
34414         
34415         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34416         
34417         this.columnWidth += padExtra
34418         //this.padWidth = Math.floor(padavail /  ( this.cols));
34419         
34420         // adjust colum width so that padding is fixed??
34421         
34422         // we have 3 columns ... total = width * 3
34423         // we have X left over... that should be used by 
34424         
34425         //if (this.expandC) {
34426             
34427         //}
34428         
34429         
34430         
34431     },
34432     
34433     getContainerWidth : function()
34434     {
34435        /* // container is parent if fit width
34436         var container = this.isFitWidth ? this.element.parentNode : this.element;
34437         // check that this.size and size are there
34438         // IE8 triggers resize on body size change, so they might not be
34439         
34440         var size = getSize( container );  //FIXME
34441         this.containerWidth = size && size.innerWidth; //FIXME
34442         */
34443          
34444         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34445         
34446     },
34447     
34448     _getItemLayoutPosition : function( item )  // what is item?
34449     {
34450         // we resize the item to our columnWidth..
34451       
34452         item.setWidth(this.columnWidth);
34453         item.autoBoxAdjust  = false;
34454         
34455         var sz = item.getSize();
34456  
34457         // how many columns does this brick span
34458         var remainder = this.containerWidth % this.columnWidth;
34459         
34460         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34461         // round if off by 1 pixel, otherwise use ceil
34462         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34463         colSpan = Math.min( colSpan, this.cols );
34464         
34465         // normally this should be '1' as we dont' currently allow multi width columns..
34466         
34467         var colGroup = this._getColGroup( colSpan );
34468         // get the minimum Y value from the columns
34469         var minimumY = Math.min.apply( Math, colGroup );
34470         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34471         
34472         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34473          
34474         // position the brick
34475         var position = {
34476             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34477             y: this.currentSize.y + minimumY + this.padHeight
34478         };
34479         
34480         Roo.log(position);
34481         // apply setHeight to necessary columns
34482         var setHeight = minimumY + sz.height + this.padHeight;
34483         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34484         
34485         var setSpan = this.cols + 1 - colGroup.length;
34486         for ( var i = 0; i < setSpan; i++ ) {
34487           this.colYs[ shortColIndex + i ] = setHeight ;
34488         }
34489       
34490         return position;
34491     },
34492     
34493     /**
34494      * @param {Number} colSpan - number of columns the element spans
34495      * @returns {Array} colGroup
34496      */
34497     _getColGroup : function( colSpan )
34498     {
34499         if ( colSpan < 2 ) {
34500           // if brick spans only one column, use all the column Ys
34501           return this.colYs;
34502         }
34503       
34504         var colGroup = [];
34505         // how many different places could this brick fit horizontally
34506         var groupCount = this.cols + 1 - colSpan;
34507         // for each group potential horizontal position
34508         for ( var i = 0; i < groupCount; i++ ) {
34509           // make an array of colY values for that one group
34510           var groupColYs = this.colYs.slice( i, i + colSpan );
34511           // and get the max value of the array
34512           colGroup[i] = Math.max.apply( Math, groupColYs );
34513         }
34514         return colGroup;
34515     },
34516     /*
34517     _manageStamp : function( stamp )
34518     {
34519         var stampSize =  stamp.getSize();
34520         var offset = stamp.getBox();
34521         // get the columns that this stamp affects
34522         var firstX = this.isOriginLeft ? offset.x : offset.right;
34523         var lastX = firstX + stampSize.width;
34524         var firstCol = Math.floor( firstX / this.columnWidth );
34525         firstCol = Math.max( 0, firstCol );
34526         
34527         var lastCol = Math.floor( lastX / this.columnWidth );
34528         // lastCol should not go over if multiple of columnWidth #425
34529         lastCol -= lastX % this.columnWidth ? 0 : 1;
34530         lastCol = Math.min( this.cols - 1, lastCol );
34531         
34532         // set colYs to bottom of the stamp
34533         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34534             stampSize.height;
34535             
34536         for ( var i = firstCol; i <= lastCol; i++ ) {
34537           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34538         }
34539     },
34540     */
34541     
34542     _getContainerSize : function()
34543     {
34544         this.maxY = Math.max.apply( Math, this.colYs );
34545         var size = {
34546             height: this.maxY
34547         };
34548       
34549         if ( this.isFitWidth ) {
34550             size.width = this._getContainerFitWidth();
34551         }
34552       
34553         return size;
34554     },
34555     
34556     _getContainerFitWidth : function()
34557     {
34558         var unusedCols = 0;
34559         // count unused columns
34560         var i = this.cols;
34561         while ( --i ) {
34562           if ( this.colYs[i] !== 0 ) {
34563             break;
34564           }
34565           unusedCols++;
34566         }
34567         // fit container to columns that have been used
34568         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34569     },
34570     
34571     needsResizeLayout : function()
34572     {
34573         var previousWidth = this.containerWidth;
34574         this.getContainerWidth();
34575         return previousWidth !== this.containerWidth;
34576     }
34577  
34578 });
34579
34580  
34581
34582  /*
34583  * - LGPL
34584  *
34585  * element
34586  * 
34587  */
34588
34589 /**
34590  * @class Roo.bootstrap.MasonryBrick
34591  * @extends Roo.bootstrap.Component
34592  * Bootstrap MasonryBrick class
34593  * 
34594  * @constructor
34595  * Create a new MasonryBrick
34596  * @param {Object} config The config object
34597  */
34598
34599 Roo.bootstrap.MasonryBrick = function(config){
34600     
34601     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34602     
34603     Roo.bootstrap.MasonryBrick.register(this);
34604     
34605     this.addEvents({
34606         // raw events
34607         /**
34608          * @event click
34609          * When a MasonryBrick is clcik
34610          * @param {Roo.bootstrap.MasonryBrick} this
34611          * @param {Roo.EventObject} e
34612          */
34613         "click" : true
34614     });
34615 };
34616
34617 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
34618     
34619     /**
34620      * @cfg {String} title
34621      */   
34622     title : '',
34623     /**
34624      * @cfg {String} html
34625      */   
34626     html : '',
34627     /**
34628      * @cfg {String} bgimage
34629      */   
34630     bgimage : '',
34631     /**
34632      * @cfg {String} videourl
34633      */   
34634     videourl : '',
34635     /**
34636      * @cfg {String} cls
34637      */   
34638     cls : '',
34639     /**
34640      * @cfg {String} href
34641      */   
34642     href : '',
34643     /**
34644      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34645      */   
34646     size : 'xs',
34647     
34648     /**
34649      * @cfg {String} placetitle (center|bottom)
34650      */   
34651     placetitle : '',
34652     
34653     /**
34654      * @cfg {Boolean} isFitContainer defalut true
34655      */   
34656     isFitContainer : true, 
34657     
34658     /**
34659      * @cfg {Boolean} preventDefault defalut false
34660      */   
34661     preventDefault : false, 
34662     
34663     /**
34664      * @cfg {Boolean} inverse defalut false
34665      */   
34666     maskInverse : false, 
34667     
34668     getAutoCreate : function()
34669     {
34670         if(!this.isFitContainer){
34671             return this.getSplitAutoCreate();
34672         }
34673         
34674         var cls = 'masonry-brick masonry-brick-full';
34675         
34676         if(this.href.length){
34677             cls += ' masonry-brick-link';
34678         }
34679         
34680         if(this.bgimage.length){
34681             cls += ' masonry-brick-image';
34682         }
34683         
34684         if(this.maskInverse){
34685             cls += ' mask-inverse';
34686         }
34687         
34688         if(!this.html.length && !this.maskInverse && !this.videourl.length){
34689             cls += ' enable-mask';
34690         }
34691         
34692         if(this.size){
34693             cls += ' masonry-' + this.size + '-brick';
34694         }
34695         
34696         if(this.placetitle.length){
34697             
34698             switch (this.placetitle) {
34699                 case 'center' :
34700                     cls += ' masonry-center-title';
34701                     break;
34702                 case 'bottom' :
34703                     cls += ' masonry-bottom-title';
34704                     break;
34705                 default:
34706                     break;
34707             }
34708             
34709         } else {
34710             if(!this.html.length && !this.bgimage.length){
34711                 cls += ' masonry-center-title';
34712             }
34713
34714             if(!this.html.length && this.bgimage.length){
34715                 cls += ' masonry-bottom-title';
34716             }
34717         }
34718         
34719         if(this.cls){
34720             cls += ' ' + this.cls;
34721         }
34722         
34723         var cfg = {
34724             tag: (this.href.length) ? 'a' : 'div',
34725             cls: cls,
34726             cn: [
34727                 {
34728                     tag: 'div',
34729                     cls: 'masonry-brick-mask'
34730                 },
34731                 {
34732                     tag: 'div',
34733                     cls: 'masonry-brick-paragraph',
34734                     cn: []
34735                 }
34736             ]
34737         };
34738         
34739         if(this.href.length){
34740             cfg.href = this.href;
34741         }
34742         
34743         var cn = cfg.cn[1].cn;
34744         
34745         if(this.title.length){
34746             cn.push({
34747                 tag: 'h4',
34748                 cls: 'masonry-brick-title',
34749                 html: this.title
34750             });
34751         }
34752         
34753         if(this.html.length){
34754             cn.push({
34755                 tag: 'p',
34756                 cls: 'masonry-brick-text',
34757                 html: this.html
34758             });
34759         }
34760         
34761         if (!this.title.length && !this.html.length) {
34762             cfg.cn[1].cls += ' hide';
34763         }
34764         
34765         if(this.bgimage.length){
34766             cfg.cn.push({
34767                 tag: 'img',
34768                 cls: 'masonry-brick-image-view',
34769                 src: this.bgimage
34770             });
34771         }
34772         
34773         if(this.videourl.length){
34774             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34775             // youtube support only?
34776             cfg.cn.push({
34777                 tag: 'iframe',
34778                 cls: 'masonry-brick-image-view',
34779                 src: vurl,
34780                 frameborder : 0,
34781                 allowfullscreen : true
34782             });
34783         }
34784         
34785         return cfg;
34786         
34787     },
34788     
34789     getSplitAutoCreate : function()
34790     {
34791         var cls = 'masonry-brick masonry-brick-split';
34792         
34793         if(this.href.length){
34794             cls += ' masonry-brick-link';
34795         }
34796         
34797         if(this.bgimage.length){
34798             cls += ' masonry-brick-image';
34799         }
34800         
34801         if(this.size){
34802             cls += ' masonry-' + this.size + '-brick';
34803         }
34804         
34805         switch (this.placetitle) {
34806             case 'center' :
34807                 cls += ' masonry-center-title';
34808                 break;
34809             case 'bottom' :
34810                 cls += ' masonry-bottom-title';
34811                 break;
34812             default:
34813                 if(!this.bgimage.length){
34814                     cls += ' masonry-center-title';
34815                 }
34816
34817                 if(this.bgimage.length){
34818                     cls += ' masonry-bottom-title';
34819                 }
34820                 break;
34821         }
34822         
34823         if(this.cls){
34824             cls += ' ' + this.cls;
34825         }
34826         
34827         var cfg = {
34828             tag: (this.href.length) ? 'a' : 'div',
34829             cls: cls,
34830             cn: [
34831                 {
34832                     tag: 'div',
34833                     cls: 'masonry-brick-split-head',
34834                     cn: [
34835                         {
34836                             tag: 'div',
34837                             cls: 'masonry-brick-paragraph',
34838                             cn: []
34839                         }
34840                     ]
34841                 },
34842                 {
34843                     tag: 'div',
34844                     cls: 'masonry-brick-split-body',
34845                     cn: []
34846                 }
34847             ]
34848         };
34849         
34850         if(this.href.length){
34851             cfg.href = this.href;
34852         }
34853         
34854         if(this.title.length){
34855             cfg.cn[0].cn[0].cn.push({
34856                 tag: 'h4',
34857                 cls: 'masonry-brick-title',
34858                 html: this.title
34859             });
34860         }
34861         
34862         if(this.html.length){
34863             cfg.cn[1].cn.push({
34864                 tag: 'p',
34865                 cls: 'masonry-brick-text',
34866                 html: this.html
34867             });
34868         }
34869
34870         if(this.bgimage.length){
34871             cfg.cn[0].cn.push({
34872                 tag: 'img',
34873                 cls: 'masonry-brick-image-view',
34874                 src: this.bgimage
34875             });
34876         }
34877         
34878         if(this.videourl.length){
34879             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34880             // youtube support only?
34881             cfg.cn[0].cn.cn.push({
34882                 tag: 'iframe',
34883                 cls: 'masonry-brick-image-view',
34884                 src: vurl,
34885                 frameborder : 0,
34886                 allowfullscreen : true
34887             });
34888         }
34889         
34890         return cfg;
34891     },
34892     
34893     initEvents: function() 
34894     {
34895         switch (this.size) {
34896             case 'xs' :
34897                 this.x = 1;
34898                 this.y = 1;
34899                 break;
34900             case 'sm' :
34901                 this.x = 2;
34902                 this.y = 2;
34903                 break;
34904             case 'md' :
34905             case 'md-left' :
34906             case 'md-right' :
34907                 this.x = 3;
34908                 this.y = 3;
34909                 break;
34910             case 'tall' :
34911                 this.x = 2;
34912                 this.y = 3;
34913                 break;
34914             case 'wide' :
34915                 this.x = 3;
34916                 this.y = 2;
34917                 break;
34918             case 'wide-thin' :
34919                 this.x = 3;
34920                 this.y = 1;
34921                 break;
34922                         
34923             default :
34924                 break;
34925         }
34926         
34927         if(Roo.isTouch){
34928             this.el.on('touchstart', this.onTouchStart, this);
34929             this.el.on('touchmove', this.onTouchMove, this);
34930             this.el.on('touchend', this.onTouchEnd, this);
34931             this.el.on('contextmenu', this.onContextMenu, this);
34932         } else {
34933             this.el.on('mouseenter'  ,this.enter, this);
34934             this.el.on('mouseleave', this.leave, this);
34935             this.el.on('click', this.onClick, this);
34936         }
34937         
34938         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
34939             this.parent().bricks.push(this);   
34940         }
34941         
34942     },
34943     
34944     onClick: function(e, el)
34945     {
34946         var time = this.endTimer - this.startTimer;
34947         // Roo.log(e.preventDefault());
34948         if(Roo.isTouch){
34949             if(time > 1000){
34950                 e.preventDefault();
34951                 return;
34952             }
34953         }
34954         
34955         if(!this.preventDefault){
34956             return;
34957         }
34958         
34959         e.preventDefault();
34960         
34961         if (this.activeClass != '') {
34962             this.selectBrick();
34963         }
34964         
34965         this.fireEvent('click', this, e);
34966     },
34967     
34968     enter: function(e, el)
34969     {
34970         e.preventDefault();
34971         
34972         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
34973             return;
34974         }
34975         
34976         if(this.bgimage.length && this.html.length){
34977             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
34978         }
34979     },
34980     
34981     leave: function(e, el)
34982     {
34983         e.preventDefault();
34984         
34985         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
34986             return;
34987         }
34988         
34989         if(this.bgimage.length && this.html.length){
34990             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
34991         }
34992     },
34993     
34994     onTouchStart: function(e, el)
34995     {
34996 //        e.preventDefault();
34997         
34998         this.touchmoved = false;
34999         
35000         if(!this.isFitContainer){
35001             return;
35002         }
35003         
35004         if(!this.bgimage.length || !this.html.length){
35005             return;
35006         }
35007         
35008         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35009         
35010         this.timer = new Date().getTime();
35011         
35012     },
35013     
35014     onTouchMove: function(e, el)
35015     {
35016         this.touchmoved = true;
35017     },
35018     
35019     onContextMenu : function(e,el)
35020     {
35021         e.preventDefault();
35022         e.stopPropagation();
35023         return false;
35024     },
35025     
35026     onTouchEnd: function(e, el)
35027     {
35028 //        e.preventDefault();
35029         
35030         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35031         
35032             this.leave(e,el);
35033             
35034             return;
35035         }
35036         
35037         if(!this.bgimage.length || !this.html.length){
35038             
35039             if(this.href.length){
35040                 window.location.href = this.href;
35041             }
35042             
35043             return;
35044         }
35045         
35046         if(!this.isFitContainer){
35047             return;
35048         }
35049         
35050         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35051         
35052         window.location.href = this.href;
35053     },
35054     
35055     //selection on single brick only
35056     selectBrick : function() {
35057         
35058         if (!this.parentId) {
35059             return;
35060         }
35061         
35062         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35063         var index = m.selectedBrick.indexOf(this.id);
35064         
35065         if ( index > -1) {
35066             m.selectedBrick.splice(index,1);
35067             this.el.removeClass(this.activeClass);
35068             return;
35069         }
35070         
35071         for(var i = 0; i < m.selectedBrick.length; i++) {
35072             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35073             b.el.removeClass(b.activeClass);
35074         }
35075         
35076         m.selectedBrick = [];
35077         
35078         m.selectedBrick.push(this.id);
35079         this.el.addClass(this.activeClass);
35080         return;
35081     },
35082     
35083     isSelected : function(){
35084         return this.el.hasClass(this.activeClass);
35085         
35086     }
35087 });
35088
35089 Roo.apply(Roo.bootstrap.MasonryBrick, {
35090     
35091     //groups: {},
35092     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35093      /**
35094     * register a Masonry Brick
35095     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35096     */
35097     
35098     register : function(brick)
35099     {
35100         //this.groups[brick.id] = brick;
35101         this.groups.add(brick.id, brick);
35102     },
35103     /**
35104     * fetch a  masonry brick based on the masonry brick ID
35105     * @param {string} the masonry brick to add
35106     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35107     */
35108     
35109     get: function(brick_id) 
35110     {
35111         // if (typeof(this.groups[brick_id]) == 'undefined') {
35112         //     return false;
35113         // }
35114         // return this.groups[brick_id] ;
35115         
35116         if(this.groups.key(brick_id)) {
35117             return this.groups.key(brick_id);
35118         }
35119         
35120         return false;
35121     }
35122     
35123     
35124     
35125 });
35126
35127  /*
35128  * - LGPL
35129  *
35130  * element
35131  * 
35132  */
35133
35134 /**
35135  * @class Roo.bootstrap.Brick
35136  * @extends Roo.bootstrap.Component
35137  * Bootstrap Brick class
35138  * 
35139  * @constructor
35140  * Create a new Brick
35141  * @param {Object} config The config object
35142  */
35143
35144 Roo.bootstrap.Brick = function(config){
35145     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35146     
35147     this.addEvents({
35148         // raw events
35149         /**
35150          * @event click
35151          * When a Brick is click
35152          * @param {Roo.bootstrap.Brick} this
35153          * @param {Roo.EventObject} e
35154          */
35155         "click" : true
35156     });
35157 };
35158
35159 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35160     
35161     /**
35162      * @cfg {String} title
35163      */   
35164     title : '',
35165     /**
35166      * @cfg {String} html
35167      */   
35168     html : '',
35169     /**
35170      * @cfg {String} bgimage
35171      */   
35172     bgimage : '',
35173     /**
35174      * @cfg {String} cls
35175      */   
35176     cls : '',
35177     /**
35178      * @cfg {String} href
35179      */   
35180     href : '',
35181     /**
35182      * @cfg {String} video
35183      */   
35184     video : '',
35185     /**
35186      * @cfg {Boolean} square
35187      */   
35188     square : true,
35189     
35190     getAutoCreate : function()
35191     {
35192         var cls = 'roo-brick';
35193         
35194         if(this.href.length){
35195             cls += ' roo-brick-link';
35196         }
35197         
35198         if(this.bgimage.length){
35199             cls += ' roo-brick-image';
35200         }
35201         
35202         if(!this.html.length && !this.bgimage.length){
35203             cls += ' roo-brick-center-title';
35204         }
35205         
35206         if(!this.html.length && this.bgimage.length){
35207             cls += ' roo-brick-bottom-title';
35208         }
35209         
35210         if(this.cls){
35211             cls += ' ' + this.cls;
35212         }
35213         
35214         var cfg = {
35215             tag: (this.href.length) ? 'a' : 'div',
35216             cls: cls,
35217             cn: [
35218                 {
35219                     tag: 'div',
35220                     cls: 'roo-brick-paragraph',
35221                     cn: []
35222                 }
35223             ]
35224         };
35225         
35226         if(this.href.length){
35227             cfg.href = this.href;
35228         }
35229         
35230         var cn = cfg.cn[0].cn;
35231         
35232         if(this.title.length){
35233             cn.push({
35234                 tag: 'h4',
35235                 cls: 'roo-brick-title',
35236                 html: this.title
35237             });
35238         }
35239         
35240         if(this.html.length){
35241             cn.push({
35242                 tag: 'p',
35243                 cls: 'roo-brick-text',
35244                 html: this.html
35245             });
35246         } else {
35247             cn.cls += ' hide';
35248         }
35249         
35250         if(this.bgimage.length){
35251             cfg.cn.push({
35252                 tag: 'img',
35253                 cls: 'roo-brick-image-view',
35254                 src: this.bgimage
35255             });
35256         }
35257         
35258         return cfg;
35259     },
35260     
35261     initEvents: function() 
35262     {
35263         if(this.title.length || this.html.length){
35264             this.el.on('mouseenter'  ,this.enter, this);
35265             this.el.on('mouseleave', this.leave, this);
35266         }
35267         
35268         Roo.EventManager.onWindowResize(this.resize, this); 
35269         
35270         if(this.bgimage.length){
35271             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35272             this.imageEl.on('load', this.onImageLoad, this);
35273             return;
35274         }
35275         
35276         this.resize();
35277     },
35278     
35279     onImageLoad : function()
35280     {
35281         this.resize();
35282     },
35283     
35284     resize : function()
35285     {
35286         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35287         
35288         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35289         
35290         if(this.bgimage.length){
35291             var image = this.el.select('.roo-brick-image-view', true).first();
35292             
35293             image.setWidth(paragraph.getWidth());
35294             
35295             if(this.square){
35296                 image.setHeight(paragraph.getWidth());
35297             }
35298             
35299             this.el.setHeight(image.getHeight());
35300             paragraph.setHeight(image.getHeight());
35301             
35302         }
35303         
35304     },
35305     
35306     enter: function(e, el)
35307     {
35308         e.preventDefault();
35309         
35310         if(this.bgimage.length){
35311             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35312             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35313         }
35314     },
35315     
35316     leave: function(e, el)
35317     {
35318         e.preventDefault();
35319         
35320         if(this.bgimage.length){
35321             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35322             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35323         }
35324     }
35325     
35326 });
35327
35328  
35329
35330  /*
35331  * - LGPL
35332  *
35333  * Number field 
35334  */
35335
35336 /**
35337  * @class Roo.bootstrap.NumberField
35338  * @extends Roo.bootstrap.Input
35339  * Bootstrap NumberField class
35340  * 
35341  * 
35342  * 
35343  * 
35344  * @constructor
35345  * Create a new NumberField
35346  * @param {Object} config The config object
35347  */
35348
35349 Roo.bootstrap.NumberField = function(config){
35350     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35351 };
35352
35353 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35354     
35355     /**
35356      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35357      */
35358     allowDecimals : true,
35359     /**
35360      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35361      */
35362     decimalSeparator : ".",
35363     /**
35364      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35365      */
35366     decimalPrecision : 2,
35367     /**
35368      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35369      */
35370     allowNegative : true,
35371     
35372     /**
35373      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35374      */
35375     allowZero: true,
35376     /**
35377      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35378      */
35379     minValue : Number.NEGATIVE_INFINITY,
35380     /**
35381      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35382      */
35383     maxValue : Number.MAX_VALUE,
35384     /**
35385      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35386      */
35387     minText : "The minimum value for this field is {0}",
35388     /**
35389      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35390      */
35391     maxText : "The maximum value for this field is {0}",
35392     /**
35393      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35394      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35395      */
35396     nanText : "{0} is not a valid number",
35397     /**
35398      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35399      */
35400     thousandsDelimiter : false,
35401     /**
35402      * @cfg {String} valueAlign alignment of value
35403      */
35404     valueAlign : "left",
35405
35406     getAutoCreate : function()
35407     {
35408         var hiddenInput = {
35409             tag: 'input',
35410             type: 'hidden',
35411             id: Roo.id(),
35412             cls: 'hidden-number-input'
35413         };
35414         
35415         if (this.name) {
35416             hiddenInput.name = this.name;
35417         }
35418         
35419         this.name = '';
35420         
35421         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35422         
35423         this.name = hiddenInput.name;
35424         
35425         if(cfg.cn.length > 0) {
35426             cfg.cn.push(hiddenInput);
35427         }
35428         
35429         return cfg;
35430     },
35431
35432     // private
35433     initEvents : function()
35434     {   
35435         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35436         
35437         var allowed = "0123456789";
35438         
35439         if(this.allowDecimals){
35440             allowed += this.decimalSeparator;
35441         }
35442         
35443         if(this.allowNegative){
35444             allowed += "-";
35445         }
35446         
35447         if(this.thousandsDelimiter) {
35448             allowed += ",";
35449         }
35450         
35451         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35452         
35453         var keyPress = function(e){
35454             
35455             var k = e.getKey();
35456             
35457             var c = e.getCharCode();
35458             
35459             if(
35460                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35461                     allowed.indexOf(String.fromCharCode(c)) === -1
35462             ){
35463                 e.stopEvent();
35464                 return;
35465             }
35466             
35467             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35468                 return;
35469             }
35470             
35471             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35472                 e.stopEvent();
35473             }
35474         };
35475         
35476         this.el.on("keypress", keyPress, this);
35477     },
35478     
35479     validateValue : function(value)
35480     {
35481         
35482         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35483             return false;
35484         }
35485         
35486         var num = this.parseValue(value);
35487         
35488         if(isNaN(num)){
35489             this.markInvalid(String.format(this.nanText, value));
35490             return false;
35491         }
35492         
35493         if(num < this.minValue){
35494             this.markInvalid(String.format(this.minText, this.minValue));
35495             return false;
35496         }
35497         
35498         if(num > this.maxValue){
35499             this.markInvalid(String.format(this.maxText, this.maxValue));
35500             return false;
35501         }
35502         
35503         return true;
35504     },
35505
35506     getValue : function()
35507     {
35508         var v = this.hiddenEl().getValue();
35509         
35510         return this.fixPrecision(this.parseValue(v));
35511     },
35512
35513     parseValue : function(value)
35514     {
35515         if(this.thousandsDelimiter) {
35516             value += "";
35517             r = new RegExp(",", "g");
35518             value = value.replace(r, "");
35519         }
35520         
35521         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35522         return isNaN(value) ? '' : value;
35523     },
35524
35525     fixPrecision : function(value)
35526     {
35527         if(this.thousandsDelimiter) {
35528             value += "";
35529             r = new RegExp(",", "g");
35530             value = value.replace(r, "");
35531         }
35532         
35533         var nan = isNaN(value);
35534         
35535         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35536             return nan ? '' : value;
35537         }
35538         return parseFloat(value).toFixed(this.decimalPrecision);
35539     },
35540
35541     setValue : function(v)
35542     {
35543         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35544         
35545         this.value = v;
35546         
35547         if(this.rendered){
35548             
35549             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35550             
35551             this.inputEl().dom.value = (v == '') ? '' :
35552                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35553             
35554             if(!this.allowZero && v === '0') {
35555                 this.hiddenEl().dom.value = '';
35556                 this.inputEl().dom.value = '';
35557             }
35558             
35559             this.validate();
35560         }
35561     },
35562
35563     decimalPrecisionFcn : function(v)
35564     {
35565         return Math.floor(v);
35566     },
35567
35568     beforeBlur : function()
35569     {
35570         var v = this.parseValue(this.getRawValue());
35571         
35572         if(v || v === 0 || v === ''){
35573             this.setValue(v);
35574         }
35575     },
35576     
35577     hiddenEl : function()
35578     {
35579         return this.el.select('input.hidden-number-input',true).first();
35580     }
35581     
35582 });
35583
35584  
35585
35586 /*
35587 * Licence: LGPL
35588 */
35589
35590 /**
35591  * @class Roo.bootstrap.DocumentSlider
35592  * @extends Roo.bootstrap.Component
35593  * Bootstrap DocumentSlider class
35594  * 
35595  * @constructor
35596  * Create a new DocumentViewer
35597  * @param {Object} config The config object
35598  */
35599
35600 Roo.bootstrap.DocumentSlider = function(config){
35601     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35602     
35603     this.files = [];
35604     
35605     this.addEvents({
35606         /**
35607          * @event initial
35608          * Fire after initEvent
35609          * @param {Roo.bootstrap.DocumentSlider} this
35610          */
35611         "initial" : true,
35612         /**
35613          * @event update
35614          * Fire after update
35615          * @param {Roo.bootstrap.DocumentSlider} this
35616          */
35617         "update" : true,
35618         /**
35619          * @event click
35620          * Fire after click
35621          * @param {Roo.bootstrap.DocumentSlider} this
35622          */
35623         "click" : true
35624     });
35625 };
35626
35627 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
35628     
35629     files : false,
35630     
35631     indicator : 0,
35632     
35633     getAutoCreate : function()
35634     {
35635         var cfg = {
35636             tag : 'div',
35637             cls : 'roo-document-slider',
35638             cn : [
35639                 {
35640                     tag : 'div',
35641                     cls : 'roo-document-slider-header',
35642                     cn : [
35643                         {
35644                             tag : 'div',
35645                             cls : 'roo-document-slider-header-title'
35646                         }
35647                     ]
35648                 },
35649                 {
35650                     tag : 'div',
35651                     cls : 'roo-document-slider-body',
35652                     cn : [
35653                         {
35654                             tag : 'div',
35655                             cls : 'roo-document-slider-prev',
35656                             cn : [
35657                                 {
35658                                     tag : 'i',
35659                                     cls : 'fa fa-chevron-left'
35660                                 }
35661                             ]
35662                         },
35663                         {
35664                             tag : 'div',
35665                             cls : 'roo-document-slider-thumb',
35666                             cn : [
35667                                 {
35668                                     tag : 'img',
35669                                     cls : 'roo-document-slider-image'
35670                                 }
35671                             ]
35672                         },
35673                         {
35674                             tag : 'div',
35675                             cls : 'roo-document-slider-next',
35676                             cn : [
35677                                 {
35678                                     tag : 'i',
35679                                     cls : 'fa fa-chevron-right'
35680                                 }
35681                             ]
35682                         }
35683                     ]
35684                 }
35685             ]
35686         };
35687         
35688         return cfg;
35689     },
35690     
35691     initEvents : function()
35692     {
35693         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35694         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35695         
35696         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35697         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35698         
35699         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35700         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35701         
35702         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35703         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35704         
35705         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35706         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35707         
35708         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35709         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35710         
35711         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35712         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35713         
35714         this.thumbEl.on('click', this.onClick, this);
35715         
35716         this.prevIndicator.on('click', this.prev, this);
35717         
35718         this.nextIndicator.on('click', this.next, this);
35719         
35720     },
35721     
35722     initial : function()
35723     {
35724         if(this.files.length){
35725             this.indicator = 1;
35726             this.update()
35727         }
35728         
35729         this.fireEvent('initial', this);
35730     },
35731     
35732     update : function()
35733     {
35734         this.imageEl.attr('src', this.files[this.indicator - 1]);
35735         
35736         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35737         
35738         this.prevIndicator.show();
35739         
35740         if(this.indicator == 1){
35741             this.prevIndicator.hide();
35742         }
35743         
35744         this.nextIndicator.show();
35745         
35746         if(this.indicator == this.files.length){
35747             this.nextIndicator.hide();
35748         }
35749         
35750         this.thumbEl.scrollTo('top');
35751         
35752         this.fireEvent('update', this);
35753     },
35754     
35755     onClick : function(e)
35756     {
35757         e.preventDefault();
35758         
35759         this.fireEvent('click', this);
35760     },
35761     
35762     prev : function(e)
35763     {
35764         e.preventDefault();
35765         
35766         this.indicator = Math.max(1, this.indicator - 1);
35767         
35768         this.update();
35769     },
35770     
35771     next : function(e)
35772     {
35773         e.preventDefault();
35774         
35775         this.indicator = Math.min(this.files.length, this.indicator + 1);
35776         
35777         this.update();
35778     }
35779 });
35780 /*
35781  * - LGPL
35782  *
35783  * RadioSet
35784  *
35785  *
35786  */
35787
35788 /**
35789  * @class Roo.bootstrap.RadioSet
35790  * @extends Roo.bootstrap.Input
35791  * Bootstrap RadioSet class
35792  * @cfg {String} indicatorpos (left|right) default left
35793  * @cfg {Boolean} inline (true|false) inline the element (default true)
35794  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
35795  * @constructor
35796  * Create a new RadioSet
35797  * @param {Object} config The config object
35798  */
35799
35800 Roo.bootstrap.RadioSet = function(config){
35801     
35802     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
35803     
35804     this.radioes = [];
35805     
35806     Roo.bootstrap.RadioSet.register(this);
35807     
35808     this.addEvents({
35809         /**
35810         * @event check
35811         * Fires when the element is checked or unchecked.
35812         * @param {Roo.bootstrap.RadioSet} this This radio
35813         * @param {Roo.bootstrap.Radio} item The checked item
35814         */
35815        check : true,
35816        /**
35817         * @event click
35818         * Fires when the element is click.
35819         * @param {Roo.bootstrap.RadioSet} this This radio set
35820         * @param {Roo.bootstrap.Radio} item The checked item
35821         * @param {Roo.EventObject} e The event object
35822         */
35823        click : true
35824     });
35825     
35826 };
35827
35828 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
35829
35830     radioes : false,
35831     
35832     inline : true,
35833     
35834     weight : '',
35835     
35836     indicatorpos : 'left',
35837     
35838     getAutoCreate : function()
35839     {
35840         var label = {
35841             tag : 'label',
35842             cls : 'roo-radio-set-label',
35843             cn : [
35844                 {
35845                     tag : 'span',
35846                     html : this.fieldLabel
35847                 }
35848             ]
35849         };
35850         if (Roo.bootstrap.version == 3) {
35851             
35852             
35853             if(this.indicatorpos == 'left'){
35854                 label.cn.unshift({
35855                     tag : 'i',
35856                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
35857                     tooltip : 'This field is required'
35858                 });
35859             } else {
35860                 label.cn.push({
35861                     tag : 'i',
35862                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
35863                     tooltip : 'This field is required'
35864                 });
35865             }
35866         }
35867         var items = {
35868             tag : 'div',
35869             cls : 'roo-radio-set-items'
35870         };
35871         
35872         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
35873         
35874         if (align === 'left' && this.fieldLabel.length) {
35875             
35876             items = {
35877                 cls : "roo-radio-set-right", 
35878                 cn: [
35879                     items
35880                 ]
35881             };
35882             
35883             if(this.labelWidth > 12){
35884                 label.style = "width: " + this.labelWidth + 'px';
35885             }
35886             
35887             if(this.labelWidth < 13 && this.labelmd == 0){
35888                 this.labelmd = this.labelWidth;
35889             }
35890             
35891             if(this.labellg > 0){
35892                 label.cls += ' col-lg-' + this.labellg;
35893                 items.cls += ' col-lg-' + (12 - this.labellg);
35894             }
35895             
35896             if(this.labelmd > 0){
35897                 label.cls += ' col-md-' + this.labelmd;
35898                 items.cls += ' col-md-' + (12 - this.labelmd);
35899             }
35900             
35901             if(this.labelsm > 0){
35902                 label.cls += ' col-sm-' + this.labelsm;
35903                 items.cls += ' col-sm-' + (12 - this.labelsm);
35904             }
35905             
35906             if(this.labelxs > 0){
35907                 label.cls += ' col-xs-' + this.labelxs;
35908                 items.cls += ' col-xs-' + (12 - this.labelxs);
35909             }
35910         }
35911         
35912         var cfg = {
35913             tag : 'div',
35914             cls : 'roo-radio-set',
35915             cn : [
35916                 {
35917                     tag : 'input',
35918                     cls : 'roo-radio-set-input',
35919                     type : 'hidden',
35920                     name : this.name,
35921                     value : this.value ? this.value :  ''
35922                 },
35923                 label,
35924                 items
35925             ]
35926         };
35927         
35928         if(this.weight.length){
35929             cfg.cls += ' roo-radio-' + this.weight;
35930         }
35931         
35932         if(this.inline) {
35933             cfg.cls += ' roo-radio-set-inline';
35934         }
35935         
35936         var settings=this;
35937         ['xs','sm','md','lg'].map(function(size){
35938             if (settings[size]) {
35939                 cfg.cls += ' col-' + size + '-' + settings[size];
35940             }
35941         });
35942         
35943         return cfg;
35944         
35945     },
35946
35947     initEvents : function()
35948     {
35949         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
35950         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
35951         
35952         if(!this.fieldLabel.length){
35953             this.labelEl.hide();
35954         }
35955         
35956         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
35957         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
35958         
35959         this.indicator = this.indicatorEl();
35960         
35961         if(this.indicator){
35962             this.indicator.addClass('invisible');
35963         }
35964         
35965         this.originalValue = this.getValue();
35966         
35967     },
35968     
35969     inputEl: function ()
35970     {
35971         return this.el.select('.roo-radio-set-input', true).first();
35972     },
35973     
35974     getChildContainer : function()
35975     {
35976         return this.itemsEl;
35977     },
35978     
35979     register : function(item)
35980     {
35981         this.radioes.push(item);
35982         
35983     },
35984     
35985     validate : function()
35986     {   
35987         if(this.getVisibilityEl().hasClass('hidden')){
35988             return true;
35989         }
35990         
35991         var valid = false;
35992         
35993         Roo.each(this.radioes, function(i){
35994             if(!i.checked){
35995                 return;
35996             }
35997             
35998             valid = true;
35999             return false;
36000         });
36001         
36002         if(this.allowBlank) {
36003             return true;
36004         }
36005         
36006         if(this.disabled || valid){
36007             this.markValid();
36008             return true;
36009         }
36010         
36011         this.markInvalid();
36012         return false;
36013         
36014     },
36015     
36016     markValid : function()
36017     {
36018         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36019             this.indicatorEl().removeClass('visible');
36020             this.indicatorEl().addClass('invisible');
36021         }
36022         
36023         
36024         if (Roo.bootstrap.version == 3) {
36025             this.el.removeClass([this.invalidClass, this.validClass]);
36026             this.el.addClass(this.validClass);
36027         } else {
36028             this.el.removeClass(['is-invalid','is-valid']);
36029             this.el.addClass(['is-valid']);
36030         }
36031         this.fireEvent('valid', this);
36032     },
36033     
36034     markInvalid : function(msg)
36035     {
36036         if(this.allowBlank || this.disabled){
36037             return;
36038         }
36039         
36040         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36041             this.indicatorEl().removeClass('invisible');
36042             this.indicatorEl().addClass('visible');
36043         }
36044         if (Roo.bootstrap.version == 3) {
36045             this.el.removeClass([this.invalidClass, this.validClass]);
36046             this.el.addClass(this.invalidClass);
36047         } else {
36048             this.el.removeClass(['is-invalid','is-valid']);
36049             this.el.addClass(['is-invalid']);
36050         }
36051         
36052         this.fireEvent('invalid', this, msg);
36053         
36054     },
36055     
36056     setValue : function(v, suppressEvent)
36057     {   
36058         if(this.value === v){
36059             return;
36060         }
36061         
36062         this.value = v;
36063         
36064         if(this.rendered){
36065             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36066         }
36067         
36068         Roo.each(this.radioes, function(i){
36069             i.checked = false;
36070             i.el.removeClass('checked');
36071         });
36072         
36073         Roo.each(this.radioes, function(i){
36074             
36075             if(i.value === v || i.value.toString() === v.toString()){
36076                 i.checked = true;
36077                 i.el.addClass('checked');
36078                 
36079                 if(suppressEvent !== true){
36080                     this.fireEvent('check', this, i);
36081                 }
36082                 
36083                 return false;
36084             }
36085             
36086         }, this);
36087         
36088         this.validate();
36089     },
36090     
36091     clearInvalid : function(){
36092         
36093         if(!this.el || this.preventMark){
36094             return;
36095         }
36096         
36097         this.el.removeClass([this.invalidClass]);
36098         
36099         this.fireEvent('valid', this);
36100     }
36101     
36102 });
36103
36104 Roo.apply(Roo.bootstrap.RadioSet, {
36105     
36106     groups: {},
36107     
36108     register : function(set)
36109     {
36110         this.groups[set.name] = set;
36111     },
36112     
36113     get: function(name) 
36114     {
36115         if (typeof(this.groups[name]) == 'undefined') {
36116             return false;
36117         }
36118         
36119         return this.groups[name] ;
36120     }
36121     
36122 });
36123 /*
36124  * Based on:
36125  * Ext JS Library 1.1.1
36126  * Copyright(c) 2006-2007, Ext JS, LLC.
36127  *
36128  * Originally Released Under LGPL - original licence link has changed is not relivant.
36129  *
36130  * Fork - LGPL
36131  * <script type="text/javascript">
36132  */
36133
36134
36135 /**
36136  * @class Roo.bootstrap.SplitBar
36137  * @extends Roo.util.Observable
36138  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36139  * <br><br>
36140  * Usage:
36141  * <pre><code>
36142 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36143                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36144 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36145 split.minSize = 100;
36146 split.maxSize = 600;
36147 split.animate = true;
36148 split.on('moved', splitterMoved);
36149 </code></pre>
36150  * @constructor
36151  * Create a new SplitBar
36152  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36153  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36154  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36155  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36156                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36157                         position of the SplitBar).
36158  */
36159 Roo.bootstrap.SplitBar = function(cfg){
36160     
36161     /** @private */
36162     
36163     //{
36164     //  dragElement : elm
36165     //  resizingElement: el,
36166         // optional..
36167     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36168     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36169         // existingProxy ???
36170     //}
36171     
36172     this.el = Roo.get(cfg.dragElement, true);
36173     this.el.dom.unselectable = "on";
36174     /** @private */
36175     this.resizingEl = Roo.get(cfg.resizingElement, true);
36176
36177     /**
36178      * @private
36179      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36180      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36181      * @type Number
36182      */
36183     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36184     
36185     /**
36186      * The minimum size of the resizing element. (Defaults to 0)
36187      * @type Number
36188      */
36189     this.minSize = 0;
36190     
36191     /**
36192      * The maximum size of the resizing element. (Defaults to 2000)
36193      * @type Number
36194      */
36195     this.maxSize = 2000;
36196     
36197     /**
36198      * Whether to animate the transition to the new size
36199      * @type Boolean
36200      */
36201     this.animate = false;
36202     
36203     /**
36204      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36205      * @type Boolean
36206      */
36207     this.useShim = false;
36208     
36209     /** @private */
36210     this.shim = null;
36211     
36212     if(!cfg.existingProxy){
36213         /** @private */
36214         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36215     }else{
36216         this.proxy = Roo.get(cfg.existingProxy).dom;
36217     }
36218     /** @private */
36219     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36220     
36221     /** @private */
36222     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36223     
36224     /** @private */
36225     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36226     
36227     /** @private */
36228     this.dragSpecs = {};
36229     
36230     /**
36231      * @private The adapter to use to positon and resize elements
36232      */
36233     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36234     this.adapter.init(this);
36235     
36236     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36237         /** @private */
36238         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36239         this.el.addClass("roo-splitbar-h");
36240     }else{
36241         /** @private */
36242         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36243         this.el.addClass("roo-splitbar-v");
36244     }
36245     
36246     this.addEvents({
36247         /**
36248          * @event resize
36249          * Fires when the splitter is moved (alias for {@link #event-moved})
36250          * @param {Roo.bootstrap.SplitBar} this
36251          * @param {Number} newSize the new width or height
36252          */
36253         "resize" : true,
36254         /**
36255          * @event moved
36256          * Fires when the splitter is moved
36257          * @param {Roo.bootstrap.SplitBar} this
36258          * @param {Number} newSize the new width or height
36259          */
36260         "moved" : true,
36261         /**
36262          * @event beforeresize
36263          * Fires before the splitter is dragged
36264          * @param {Roo.bootstrap.SplitBar} this
36265          */
36266         "beforeresize" : true,
36267
36268         "beforeapply" : true
36269     });
36270
36271     Roo.util.Observable.call(this);
36272 };
36273
36274 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36275     onStartProxyDrag : function(x, y){
36276         this.fireEvent("beforeresize", this);
36277         if(!this.overlay){
36278             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36279             o.unselectable();
36280             o.enableDisplayMode("block");
36281             // all splitbars share the same overlay
36282             Roo.bootstrap.SplitBar.prototype.overlay = o;
36283         }
36284         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36285         this.overlay.show();
36286         Roo.get(this.proxy).setDisplayed("block");
36287         var size = this.adapter.getElementSize(this);
36288         this.activeMinSize = this.getMinimumSize();;
36289         this.activeMaxSize = this.getMaximumSize();;
36290         var c1 = size - this.activeMinSize;
36291         var c2 = Math.max(this.activeMaxSize - size, 0);
36292         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36293             this.dd.resetConstraints();
36294             this.dd.setXConstraint(
36295                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36296                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36297             );
36298             this.dd.setYConstraint(0, 0);
36299         }else{
36300             this.dd.resetConstraints();
36301             this.dd.setXConstraint(0, 0);
36302             this.dd.setYConstraint(
36303                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36304                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36305             );
36306          }
36307         this.dragSpecs.startSize = size;
36308         this.dragSpecs.startPoint = [x, y];
36309         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36310     },
36311     
36312     /** 
36313      * @private Called after the drag operation by the DDProxy
36314      */
36315     onEndProxyDrag : function(e){
36316         Roo.get(this.proxy).setDisplayed(false);
36317         var endPoint = Roo.lib.Event.getXY(e);
36318         if(this.overlay){
36319             this.overlay.hide();
36320         }
36321         var newSize;
36322         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36323             newSize = this.dragSpecs.startSize + 
36324                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36325                     endPoint[0] - this.dragSpecs.startPoint[0] :
36326                     this.dragSpecs.startPoint[0] - endPoint[0]
36327                 );
36328         }else{
36329             newSize = this.dragSpecs.startSize + 
36330                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36331                     endPoint[1] - this.dragSpecs.startPoint[1] :
36332                     this.dragSpecs.startPoint[1] - endPoint[1]
36333                 );
36334         }
36335         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36336         if(newSize != this.dragSpecs.startSize){
36337             if(this.fireEvent('beforeapply', this, newSize) !== false){
36338                 this.adapter.setElementSize(this, newSize);
36339                 this.fireEvent("moved", this, newSize);
36340                 this.fireEvent("resize", this, newSize);
36341             }
36342         }
36343     },
36344     
36345     /**
36346      * Get the adapter this SplitBar uses
36347      * @return The adapter object
36348      */
36349     getAdapter : function(){
36350         return this.adapter;
36351     },
36352     
36353     /**
36354      * Set the adapter this SplitBar uses
36355      * @param {Object} adapter A SplitBar adapter object
36356      */
36357     setAdapter : function(adapter){
36358         this.adapter = adapter;
36359         this.adapter.init(this);
36360     },
36361     
36362     /**
36363      * Gets the minimum size for the resizing element
36364      * @return {Number} The minimum size
36365      */
36366     getMinimumSize : function(){
36367         return this.minSize;
36368     },
36369     
36370     /**
36371      * Sets the minimum size for the resizing element
36372      * @param {Number} minSize The minimum size
36373      */
36374     setMinimumSize : function(minSize){
36375         this.minSize = minSize;
36376     },
36377     
36378     /**
36379      * Gets the maximum size for the resizing element
36380      * @return {Number} The maximum size
36381      */
36382     getMaximumSize : function(){
36383         return this.maxSize;
36384     },
36385     
36386     /**
36387      * Sets the maximum size for the resizing element
36388      * @param {Number} maxSize The maximum size
36389      */
36390     setMaximumSize : function(maxSize){
36391         this.maxSize = maxSize;
36392     },
36393     
36394     /**
36395      * Sets the initialize size for the resizing element
36396      * @param {Number} size The initial size
36397      */
36398     setCurrentSize : function(size){
36399         var oldAnimate = this.animate;
36400         this.animate = false;
36401         this.adapter.setElementSize(this, size);
36402         this.animate = oldAnimate;
36403     },
36404     
36405     /**
36406      * Destroy this splitbar. 
36407      * @param {Boolean} removeEl True to remove the element
36408      */
36409     destroy : function(removeEl){
36410         if(this.shim){
36411             this.shim.remove();
36412         }
36413         this.dd.unreg();
36414         this.proxy.parentNode.removeChild(this.proxy);
36415         if(removeEl){
36416             this.el.remove();
36417         }
36418     }
36419 });
36420
36421 /**
36422  * @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.
36423  */
36424 Roo.bootstrap.SplitBar.createProxy = function(dir){
36425     var proxy = new Roo.Element(document.createElement("div"));
36426     proxy.unselectable();
36427     var cls = 'roo-splitbar-proxy';
36428     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36429     document.body.appendChild(proxy.dom);
36430     return proxy.dom;
36431 };
36432
36433 /** 
36434  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36435  * Default Adapter. It assumes the splitter and resizing element are not positioned
36436  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36437  */
36438 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36439 };
36440
36441 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36442     // do nothing for now
36443     init : function(s){
36444     
36445     },
36446     /**
36447      * Called before drag operations to get the current size of the resizing element. 
36448      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36449      */
36450      getElementSize : function(s){
36451         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36452             return s.resizingEl.getWidth();
36453         }else{
36454             return s.resizingEl.getHeight();
36455         }
36456     },
36457     
36458     /**
36459      * Called after drag operations to set the size of the resizing element.
36460      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36461      * @param {Number} newSize The new size to set
36462      * @param {Function} onComplete A function to be invoked when resizing is complete
36463      */
36464     setElementSize : function(s, newSize, onComplete){
36465         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36466             if(!s.animate){
36467                 s.resizingEl.setWidth(newSize);
36468                 if(onComplete){
36469                     onComplete(s, newSize);
36470                 }
36471             }else{
36472                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36473             }
36474         }else{
36475             
36476             if(!s.animate){
36477                 s.resizingEl.setHeight(newSize);
36478                 if(onComplete){
36479                     onComplete(s, newSize);
36480                 }
36481             }else{
36482                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36483             }
36484         }
36485     }
36486 };
36487
36488 /** 
36489  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36490  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36491  * Adapter that  moves the splitter element to align with the resized sizing element. 
36492  * Used with an absolute positioned SplitBar.
36493  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36494  * document.body, make sure you assign an id to the body element.
36495  */
36496 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36497     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36498     this.container = Roo.get(container);
36499 };
36500
36501 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36502     init : function(s){
36503         this.basic.init(s);
36504     },
36505     
36506     getElementSize : function(s){
36507         return this.basic.getElementSize(s);
36508     },
36509     
36510     setElementSize : function(s, newSize, onComplete){
36511         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36512     },
36513     
36514     moveSplitter : function(s){
36515         var yes = Roo.bootstrap.SplitBar;
36516         switch(s.placement){
36517             case yes.LEFT:
36518                 s.el.setX(s.resizingEl.getRight());
36519                 break;
36520             case yes.RIGHT:
36521                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36522                 break;
36523             case yes.TOP:
36524                 s.el.setY(s.resizingEl.getBottom());
36525                 break;
36526             case yes.BOTTOM:
36527                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36528                 break;
36529         }
36530     }
36531 };
36532
36533 /**
36534  * Orientation constant - Create a vertical SplitBar
36535  * @static
36536  * @type Number
36537  */
36538 Roo.bootstrap.SplitBar.VERTICAL = 1;
36539
36540 /**
36541  * Orientation constant - Create a horizontal SplitBar
36542  * @static
36543  * @type Number
36544  */
36545 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36546
36547 /**
36548  * Placement constant - The resizing element is to the left of the splitter element
36549  * @static
36550  * @type Number
36551  */
36552 Roo.bootstrap.SplitBar.LEFT = 1;
36553
36554 /**
36555  * Placement constant - The resizing element is to the right of the splitter element
36556  * @static
36557  * @type Number
36558  */
36559 Roo.bootstrap.SplitBar.RIGHT = 2;
36560
36561 /**
36562  * Placement constant - The resizing element is positioned above the splitter element
36563  * @static
36564  * @type Number
36565  */
36566 Roo.bootstrap.SplitBar.TOP = 3;
36567
36568 /**
36569  * Placement constant - The resizing element is positioned under splitter element
36570  * @static
36571  * @type Number
36572  */
36573 Roo.bootstrap.SplitBar.BOTTOM = 4;
36574 Roo.namespace("Roo.bootstrap.layout");/*
36575  * Based on:
36576  * Ext JS Library 1.1.1
36577  * Copyright(c) 2006-2007, Ext JS, LLC.
36578  *
36579  * Originally Released Under LGPL - original licence link has changed is not relivant.
36580  *
36581  * Fork - LGPL
36582  * <script type="text/javascript">
36583  */
36584
36585 /**
36586  * @class Roo.bootstrap.layout.Manager
36587  * @extends Roo.bootstrap.Component
36588  * Base class for layout managers.
36589  */
36590 Roo.bootstrap.layout.Manager = function(config)
36591 {
36592     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36593
36594
36595
36596
36597
36598     /** false to disable window resize monitoring @type Boolean */
36599     this.monitorWindowResize = true;
36600     this.regions = {};
36601     this.addEvents({
36602         /**
36603          * @event layout
36604          * Fires when a layout is performed.
36605          * @param {Roo.LayoutManager} this
36606          */
36607         "layout" : true,
36608         /**
36609          * @event regionresized
36610          * Fires when the user resizes a region.
36611          * @param {Roo.LayoutRegion} region The resized region
36612          * @param {Number} newSize The new size (width for east/west, height for north/south)
36613          */
36614         "regionresized" : true,
36615         /**
36616          * @event regioncollapsed
36617          * Fires when a region is collapsed.
36618          * @param {Roo.LayoutRegion} region The collapsed region
36619          */
36620         "regioncollapsed" : true,
36621         /**
36622          * @event regionexpanded
36623          * Fires when a region is expanded.
36624          * @param {Roo.LayoutRegion} region The expanded region
36625          */
36626         "regionexpanded" : true
36627     });
36628     this.updating = false;
36629
36630     if (config.el) {
36631         this.el = Roo.get(config.el);
36632         this.initEvents();
36633     }
36634
36635 };
36636
36637 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36638
36639
36640     regions : null,
36641
36642     monitorWindowResize : true,
36643
36644
36645     updating : false,
36646
36647
36648     onRender : function(ct, position)
36649     {
36650         if(!this.el){
36651             this.el = Roo.get(ct);
36652             this.initEvents();
36653         }
36654         //this.fireEvent('render',this);
36655     },
36656
36657
36658     initEvents: function()
36659     {
36660
36661
36662         // ie scrollbar fix
36663         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36664             document.body.scroll = "no";
36665         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36666             this.el.position('relative');
36667         }
36668         this.id = this.el.id;
36669         this.el.addClass("roo-layout-container");
36670         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36671         if(this.el.dom != document.body ) {
36672             this.el.on('resize', this.layout,this);
36673             this.el.on('show', this.layout,this);
36674         }
36675
36676     },
36677
36678     /**
36679      * Returns true if this layout is currently being updated
36680      * @return {Boolean}
36681      */
36682     isUpdating : function(){
36683         return this.updating;
36684     },
36685
36686     /**
36687      * Suspend the LayoutManager from doing auto-layouts while
36688      * making multiple add or remove calls
36689      */
36690     beginUpdate : function(){
36691         this.updating = true;
36692     },
36693
36694     /**
36695      * Restore auto-layouts and optionally disable the manager from performing a layout
36696      * @param {Boolean} noLayout true to disable a layout update
36697      */
36698     endUpdate : function(noLayout){
36699         this.updating = false;
36700         if(!noLayout){
36701             this.layout();
36702         }
36703     },
36704
36705     layout: function(){
36706         // abstract...
36707     },
36708
36709     onRegionResized : function(region, newSize){
36710         this.fireEvent("regionresized", region, newSize);
36711         this.layout();
36712     },
36713
36714     onRegionCollapsed : function(region){
36715         this.fireEvent("regioncollapsed", region);
36716     },
36717
36718     onRegionExpanded : function(region){
36719         this.fireEvent("regionexpanded", region);
36720     },
36721
36722     /**
36723      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36724      * performs box-model adjustments.
36725      * @return {Object} The size as an object {width: (the width), height: (the height)}
36726      */
36727     getViewSize : function()
36728     {
36729         var size;
36730         if(this.el.dom != document.body){
36731             size = this.el.getSize();
36732         }else{
36733             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36734         }
36735         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36736         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36737         return size;
36738     },
36739
36740     /**
36741      * Returns the Element this layout is bound to.
36742      * @return {Roo.Element}
36743      */
36744     getEl : function(){
36745         return this.el;
36746     },
36747
36748     /**
36749      * Returns the specified region.
36750      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36751      * @return {Roo.LayoutRegion}
36752      */
36753     getRegion : function(target){
36754         return this.regions[target.toLowerCase()];
36755     },
36756
36757     onWindowResize : function(){
36758         if(this.monitorWindowResize){
36759             this.layout();
36760         }
36761     }
36762 });
36763 /*
36764  * Based on:
36765  * Ext JS Library 1.1.1
36766  * Copyright(c) 2006-2007, Ext JS, LLC.
36767  *
36768  * Originally Released Under LGPL - original licence link has changed is not relivant.
36769  *
36770  * Fork - LGPL
36771  * <script type="text/javascript">
36772  */
36773 /**
36774  * @class Roo.bootstrap.layout.Border
36775  * @extends Roo.bootstrap.layout.Manager
36776  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36777  * please see: examples/bootstrap/nested.html<br><br>
36778  
36779 <b>The container the layout is rendered into can be either the body element or any other element.
36780 If it is not the body element, the container needs to either be an absolute positioned element,
36781 or you will need to add "position:relative" to the css of the container.  You will also need to specify
36782 the container size if it is not the body element.</b>
36783
36784 * @constructor
36785 * Create a new Border
36786 * @param {Object} config Configuration options
36787  */
36788 Roo.bootstrap.layout.Border = function(config){
36789     config = config || {};
36790     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
36791     
36792     
36793     
36794     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36795         if(config[region]){
36796             config[region].region = region;
36797             this.addRegion(config[region]);
36798         }
36799     },this);
36800     
36801 };
36802
36803 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
36804
36805 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
36806     
36807     parent : false, // this might point to a 'nest' or a ???
36808     
36809     /**
36810      * Creates and adds a new region if it doesn't already exist.
36811      * @param {String} target The target region key (north, south, east, west or center).
36812      * @param {Object} config The regions config object
36813      * @return {BorderLayoutRegion} The new region
36814      */
36815     addRegion : function(config)
36816     {
36817         if(!this.regions[config.region]){
36818             var r = this.factory(config);
36819             this.bindRegion(r);
36820         }
36821         return this.regions[config.region];
36822     },
36823
36824     // private (kinda)
36825     bindRegion : function(r){
36826         this.regions[r.config.region] = r;
36827         
36828         r.on("visibilitychange",    this.layout, this);
36829         r.on("paneladded",          this.layout, this);
36830         r.on("panelremoved",        this.layout, this);
36831         r.on("invalidated",         this.layout, this);
36832         r.on("resized",             this.onRegionResized, this);
36833         r.on("collapsed",           this.onRegionCollapsed, this);
36834         r.on("expanded",            this.onRegionExpanded, this);
36835     },
36836
36837     /**
36838      * Performs a layout update.
36839      */
36840     layout : function()
36841     {
36842         if(this.updating) {
36843             return;
36844         }
36845         
36846         // render all the rebions if they have not been done alreayd?
36847         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36848             if(this.regions[region] && !this.regions[region].bodyEl){
36849                 this.regions[region].onRender(this.el)
36850             }
36851         },this);
36852         
36853         var size = this.getViewSize();
36854         var w = size.width;
36855         var h = size.height;
36856         var centerW = w;
36857         var centerH = h;
36858         var centerY = 0;
36859         var centerX = 0;
36860         //var x = 0, y = 0;
36861
36862         var rs = this.regions;
36863         var north = rs["north"];
36864         var south = rs["south"]; 
36865         var west = rs["west"];
36866         var east = rs["east"];
36867         var center = rs["center"];
36868         //if(this.hideOnLayout){ // not supported anymore
36869             //c.el.setStyle("display", "none");
36870         //}
36871         if(north && north.isVisible()){
36872             var b = north.getBox();
36873             var m = north.getMargins();
36874             b.width = w - (m.left+m.right);
36875             b.x = m.left;
36876             b.y = m.top;
36877             centerY = b.height + b.y + m.bottom;
36878             centerH -= centerY;
36879             north.updateBox(this.safeBox(b));
36880         }
36881         if(south && south.isVisible()){
36882             var b = south.getBox();
36883             var m = south.getMargins();
36884             b.width = w - (m.left+m.right);
36885             b.x = m.left;
36886             var totalHeight = (b.height + m.top + m.bottom);
36887             b.y = h - totalHeight + m.top;
36888             centerH -= totalHeight;
36889             south.updateBox(this.safeBox(b));
36890         }
36891         if(west && west.isVisible()){
36892             var b = west.getBox();
36893             var m = west.getMargins();
36894             b.height = centerH - (m.top+m.bottom);
36895             b.x = m.left;
36896             b.y = centerY + m.top;
36897             var totalWidth = (b.width + m.left + m.right);
36898             centerX += totalWidth;
36899             centerW -= totalWidth;
36900             west.updateBox(this.safeBox(b));
36901         }
36902         if(east && east.isVisible()){
36903             var b = east.getBox();
36904             var m = east.getMargins();
36905             b.height = centerH - (m.top+m.bottom);
36906             var totalWidth = (b.width + m.left + m.right);
36907             b.x = w - totalWidth + m.left;
36908             b.y = centerY + m.top;
36909             centerW -= totalWidth;
36910             east.updateBox(this.safeBox(b));
36911         }
36912         if(center){
36913             var m = center.getMargins();
36914             var centerBox = {
36915                 x: centerX + m.left,
36916                 y: centerY + m.top,
36917                 width: centerW - (m.left+m.right),
36918                 height: centerH - (m.top+m.bottom)
36919             };
36920             //if(this.hideOnLayout){
36921                 //center.el.setStyle("display", "block");
36922             //}
36923             center.updateBox(this.safeBox(centerBox));
36924         }
36925         this.el.repaint();
36926         this.fireEvent("layout", this);
36927     },
36928
36929     // private
36930     safeBox : function(box){
36931         box.width = Math.max(0, box.width);
36932         box.height = Math.max(0, box.height);
36933         return box;
36934     },
36935
36936     /**
36937      * Adds a ContentPanel (or subclass) to this layout.
36938      * @param {String} target The target region key (north, south, east, west or center).
36939      * @param {Roo.ContentPanel} panel The panel to add
36940      * @return {Roo.ContentPanel} The added panel
36941      */
36942     add : function(target, panel){
36943          
36944         target = target.toLowerCase();
36945         return this.regions[target].add(panel);
36946     },
36947
36948     /**
36949      * Remove a ContentPanel (or subclass) to this layout.
36950      * @param {String} target The target region key (north, south, east, west or center).
36951      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
36952      * @return {Roo.ContentPanel} The removed panel
36953      */
36954     remove : function(target, panel){
36955         target = target.toLowerCase();
36956         return this.regions[target].remove(panel);
36957     },
36958
36959     /**
36960      * Searches all regions for a panel with the specified id
36961      * @param {String} panelId
36962      * @return {Roo.ContentPanel} The panel or null if it wasn't found
36963      */
36964     findPanel : function(panelId){
36965         var rs = this.regions;
36966         for(var target in rs){
36967             if(typeof rs[target] != "function"){
36968                 var p = rs[target].getPanel(panelId);
36969                 if(p){
36970                     return p;
36971                 }
36972             }
36973         }
36974         return null;
36975     },
36976
36977     /**
36978      * Searches all regions for a panel with the specified id and activates (shows) it.
36979      * @param {String/ContentPanel} panelId The panels id or the panel itself
36980      * @return {Roo.ContentPanel} The shown panel or null
36981      */
36982     showPanel : function(panelId) {
36983       var rs = this.regions;
36984       for(var target in rs){
36985          var r = rs[target];
36986          if(typeof r != "function"){
36987             if(r.hasPanel(panelId)){
36988                return r.showPanel(panelId);
36989             }
36990          }
36991       }
36992       return null;
36993    },
36994
36995    /**
36996      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
36997      * @param {Roo.state.Provider} provider (optional) An alternate state provider
36998      */
36999    /*
37000     restoreState : function(provider){
37001         if(!provider){
37002             provider = Roo.state.Manager;
37003         }
37004         var sm = new Roo.LayoutStateManager();
37005         sm.init(this, provider);
37006     },
37007 */
37008  
37009  
37010     /**
37011      * Adds a xtype elements to the layout.
37012      * <pre><code>
37013
37014 layout.addxtype({
37015        xtype : 'ContentPanel',
37016        region: 'west',
37017        items: [ .... ]
37018    }
37019 );
37020
37021 layout.addxtype({
37022         xtype : 'NestedLayoutPanel',
37023         region: 'west',
37024         layout: {
37025            center: { },
37026            west: { }   
37027         },
37028         items : [ ... list of content panels or nested layout panels.. ]
37029    }
37030 );
37031 </code></pre>
37032      * @param {Object} cfg Xtype definition of item to add.
37033      */
37034     addxtype : function(cfg)
37035     {
37036         // basically accepts a pannel...
37037         // can accept a layout region..!?!?
37038         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37039         
37040         
37041         // theory?  children can only be panels??
37042         
37043         //if (!cfg.xtype.match(/Panel$/)) {
37044         //    return false;
37045         //}
37046         var ret = false;
37047         
37048         if (typeof(cfg.region) == 'undefined') {
37049             Roo.log("Failed to add Panel, region was not set");
37050             Roo.log(cfg);
37051             return false;
37052         }
37053         var region = cfg.region;
37054         delete cfg.region;
37055         
37056           
37057         var xitems = [];
37058         if (cfg.items) {
37059             xitems = cfg.items;
37060             delete cfg.items;
37061         }
37062         var nb = false;
37063         
37064         if ( region == 'center') {
37065             Roo.log("Center: " + cfg.title);
37066         }
37067         
37068         
37069         switch(cfg.xtype) 
37070         {
37071             case 'Content':  // ContentPanel (el, cfg)
37072             case 'Scroll':  // ContentPanel (el, cfg)
37073             case 'View': 
37074                 cfg.autoCreate = cfg.autoCreate || true;
37075                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37076                 //} else {
37077                 //    var el = this.el.createChild();
37078                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37079                 //}
37080                 
37081                 this.add(region, ret);
37082                 break;
37083             
37084             /*
37085             case 'TreePanel': // our new panel!
37086                 cfg.el = this.el.createChild();
37087                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37088                 this.add(region, ret);
37089                 break;
37090             */
37091             
37092             case 'Nest': 
37093                 // create a new Layout (which is  a Border Layout...
37094                 
37095                 var clayout = cfg.layout;
37096                 clayout.el  = this.el.createChild();
37097                 clayout.items   = clayout.items  || [];
37098                 
37099                 delete cfg.layout;
37100                 
37101                 // replace this exitems with the clayout ones..
37102                 xitems = clayout.items;
37103                  
37104                 // force background off if it's in center...
37105                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37106                     cfg.background = false;
37107                 }
37108                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37109                 
37110                 
37111                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37112                 //console.log('adding nested layout panel '  + cfg.toSource());
37113                 this.add(region, ret);
37114                 nb = {}; /// find first...
37115                 break;
37116             
37117             case 'Grid':
37118                 
37119                 // needs grid and region
37120                 
37121                 //var el = this.getRegion(region).el.createChild();
37122                 /*
37123                  *var el = this.el.createChild();
37124                 // create the grid first...
37125                 cfg.grid.container = el;
37126                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37127                 */
37128                 
37129                 if (region == 'center' && this.active ) {
37130                     cfg.background = false;
37131                 }
37132                 
37133                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37134                 
37135                 this.add(region, ret);
37136                 /*
37137                 if (cfg.background) {
37138                     // render grid on panel activation (if panel background)
37139                     ret.on('activate', function(gp) {
37140                         if (!gp.grid.rendered) {
37141                     //        gp.grid.render(el);
37142                         }
37143                     });
37144                 } else {
37145                   //  cfg.grid.render(el);
37146                 }
37147                 */
37148                 break;
37149            
37150            
37151             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37152                 // it was the old xcomponent building that caused this before.
37153                 // espeically if border is the top element in the tree.
37154                 ret = this;
37155                 break; 
37156                 
37157                     
37158                 
37159                 
37160                 
37161             default:
37162                 /*
37163                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37164                     
37165                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37166                     this.add(region, ret);
37167                 } else {
37168                 */
37169                     Roo.log(cfg);
37170                     throw "Can not add '" + cfg.xtype + "' to Border";
37171                     return null;
37172              
37173                                 
37174              
37175         }
37176         this.beginUpdate();
37177         // add children..
37178         var region = '';
37179         var abn = {};
37180         Roo.each(xitems, function(i)  {
37181             region = nb && i.region ? i.region : false;
37182             
37183             var add = ret.addxtype(i);
37184            
37185             if (region) {
37186                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37187                 if (!i.background) {
37188                     abn[region] = nb[region] ;
37189                 }
37190             }
37191             
37192         });
37193         this.endUpdate();
37194
37195         // make the last non-background panel active..
37196         //if (nb) { Roo.log(abn); }
37197         if (nb) {
37198             
37199             for(var r in abn) {
37200                 region = this.getRegion(r);
37201                 if (region) {
37202                     // tried using nb[r], but it does not work..
37203                      
37204                     region.showPanel(abn[r]);
37205                    
37206                 }
37207             }
37208         }
37209         return ret;
37210         
37211     },
37212     
37213     
37214 // private
37215     factory : function(cfg)
37216     {
37217         
37218         var validRegions = Roo.bootstrap.layout.Border.regions;
37219
37220         var target = cfg.region;
37221         cfg.mgr = this;
37222         
37223         var r = Roo.bootstrap.layout;
37224         Roo.log(target);
37225         switch(target){
37226             case "north":
37227                 return new r.North(cfg);
37228             case "south":
37229                 return new r.South(cfg);
37230             case "east":
37231                 return new r.East(cfg);
37232             case "west":
37233                 return new r.West(cfg);
37234             case "center":
37235                 return new r.Center(cfg);
37236         }
37237         throw 'Layout region "'+target+'" not supported.';
37238     }
37239     
37240     
37241 });
37242  /*
37243  * Based on:
37244  * Ext JS Library 1.1.1
37245  * Copyright(c) 2006-2007, Ext JS, LLC.
37246  *
37247  * Originally Released Under LGPL - original licence link has changed is not relivant.
37248  *
37249  * Fork - LGPL
37250  * <script type="text/javascript">
37251  */
37252  
37253 /**
37254  * @class Roo.bootstrap.layout.Basic
37255  * @extends Roo.util.Observable
37256  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37257  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37258  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37259  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37260  * @cfg {string}   region  the region that it inhabits..
37261  * @cfg {bool}   skipConfig skip config?
37262  * 
37263
37264  */
37265 Roo.bootstrap.layout.Basic = function(config){
37266     
37267     this.mgr = config.mgr;
37268     
37269     this.position = config.region;
37270     
37271     var skipConfig = config.skipConfig;
37272     
37273     this.events = {
37274         /**
37275          * @scope Roo.BasicLayoutRegion
37276          */
37277         
37278         /**
37279          * @event beforeremove
37280          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37281          * @param {Roo.LayoutRegion} this
37282          * @param {Roo.ContentPanel} panel The panel
37283          * @param {Object} e The cancel event object
37284          */
37285         "beforeremove" : true,
37286         /**
37287          * @event invalidated
37288          * Fires when the layout for this region is changed.
37289          * @param {Roo.LayoutRegion} this
37290          */
37291         "invalidated" : true,
37292         /**
37293          * @event visibilitychange
37294          * Fires when this region is shown or hidden 
37295          * @param {Roo.LayoutRegion} this
37296          * @param {Boolean} visibility true or false
37297          */
37298         "visibilitychange" : true,
37299         /**
37300          * @event paneladded
37301          * Fires when a panel is added. 
37302          * @param {Roo.LayoutRegion} this
37303          * @param {Roo.ContentPanel} panel The panel
37304          */
37305         "paneladded" : true,
37306         /**
37307          * @event panelremoved
37308          * Fires when a panel is removed. 
37309          * @param {Roo.LayoutRegion} this
37310          * @param {Roo.ContentPanel} panel The panel
37311          */
37312         "panelremoved" : true,
37313         /**
37314          * @event beforecollapse
37315          * Fires when this region before collapse.
37316          * @param {Roo.LayoutRegion} this
37317          */
37318         "beforecollapse" : true,
37319         /**
37320          * @event collapsed
37321          * Fires when this region is collapsed.
37322          * @param {Roo.LayoutRegion} this
37323          */
37324         "collapsed" : true,
37325         /**
37326          * @event expanded
37327          * Fires when this region is expanded.
37328          * @param {Roo.LayoutRegion} this
37329          */
37330         "expanded" : true,
37331         /**
37332          * @event slideshow
37333          * Fires when this region is slid into view.
37334          * @param {Roo.LayoutRegion} this
37335          */
37336         "slideshow" : true,
37337         /**
37338          * @event slidehide
37339          * Fires when this region slides out of view. 
37340          * @param {Roo.LayoutRegion} this
37341          */
37342         "slidehide" : true,
37343         /**
37344          * @event panelactivated
37345          * Fires when a panel is activated. 
37346          * @param {Roo.LayoutRegion} this
37347          * @param {Roo.ContentPanel} panel The activated panel
37348          */
37349         "panelactivated" : true,
37350         /**
37351          * @event resized
37352          * Fires when the user resizes this region. 
37353          * @param {Roo.LayoutRegion} this
37354          * @param {Number} newSize The new size (width for east/west, height for north/south)
37355          */
37356         "resized" : true
37357     };
37358     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37359     this.panels = new Roo.util.MixedCollection();
37360     this.panels.getKey = this.getPanelId.createDelegate(this);
37361     this.box = null;
37362     this.activePanel = null;
37363     // ensure listeners are added...
37364     
37365     if (config.listeners || config.events) {
37366         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37367             listeners : config.listeners || {},
37368             events : config.events || {}
37369         });
37370     }
37371     
37372     if(skipConfig !== true){
37373         this.applyConfig(config);
37374     }
37375 };
37376
37377 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37378 {
37379     getPanelId : function(p){
37380         return p.getId();
37381     },
37382     
37383     applyConfig : function(config){
37384         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37385         this.config = config;
37386         
37387     },
37388     
37389     /**
37390      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37391      * the width, for horizontal (north, south) the height.
37392      * @param {Number} newSize The new width or height
37393      */
37394     resizeTo : function(newSize){
37395         var el = this.el ? this.el :
37396                  (this.activePanel ? this.activePanel.getEl() : null);
37397         if(el){
37398             switch(this.position){
37399                 case "east":
37400                 case "west":
37401                     el.setWidth(newSize);
37402                     this.fireEvent("resized", this, newSize);
37403                 break;
37404                 case "north":
37405                 case "south":
37406                     el.setHeight(newSize);
37407                     this.fireEvent("resized", this, newSize);
37408                 break;                
37409             }
37410         }
37411     },
37412     
37413     getBox : function(){
37414         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37415     },
37416     
37417     getMargins : function(){
37418         return this.margins;
37419     },
37420     
37421     updateBox : function(box){
37422         this.box = box;
37423         var el = this.activePanel.getEl();
37424         el.dom.style.left = box.x + "px";
37425         el.dom.style.top = box.y + "px";
37426         this.activePanel.setSize(box.width, box.height);
37427     },
37428     
37429     /**
37430      * Returns the container element for this region.
37431      * @return {Roo.Element}
37432      */
37433     getEl : function(){
37434         return this.activePanel;
37435     },
37436     
37437     /**
37438      * Returns true if this region is currently visible.
37439      * @return {Boolean}
37440      */
37441     isVisible : function(){
37442         return this.activePanel ? true : false;
37443     },
37444     
37445     setActivePanel : function(panel){
37446         panel = this.getPanel(panel);
37447         if(this.activePanel && this.activePanel != panel){
37448             this.activePanel.setActiveState(false);
37449             this.activePanel.getEl().setLeftTop(-10000,-10000);
37450         }
37451         this.activePanel = panel;
37452         panel.setActiveState(true);
37453         if(this.box){
37454             panel.setSize(this.box.width, this.box.height);
37455         }
37456         this.fireEvent("panelactivated", this, panel);
37457         this.fireEvent("invalidated");
37458     },
37459     
37460     /**
37461      * Show the specified panel.
37462      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37463      * @return {Roo.ContentPanel} The shown panel or null
37464      */
37465     showPanel : function(panel){
37466         panel = this.getPanel(panel);
37467         if(panel){
37468             this.setActivePanel(panel);
37469         }
37470         return panel;
37471     },
37472     
37473     /**
37474      * Get the active panel for this region.
37475      * @return {Roo.ContentPanel} The active panel or null
37476      */
37477     getActivePanel : function(){
37478         return this.activePanel;
37479     },
37480     
37481     /**
37482      * Add the passed ContentPanel(s)
37483      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37484      * @return {Roo.ContentPanel} The panel added (if only one was added)
37485      */
37486     add : function(panel){
37487         if(arguments.length > 1){
37488             for(var i = 0, len = arguments.length; i < len; i++) {
37489                 this.add(arguments[i]);
37490             }
37491             return null;
37492         }
37493         if(this.hasPanel(panel)){
37494             this.showPanel(panel);
37495             return panel;
37496         }
37497         var el = panel.getEl();
37498         if(el.dom.parentNode != this.mgr.el.dom){
37499             this.mgr.el.dom.appendChild(el.dom);
37500         }
37501         if(panel.setRegion){
37502             panel.setRegion(this);
37503         }
37504         this.panels.add(panel);
37505         el.setStyle("position", "absolute");
37506         if(!panel.background){
37507             this.setActivePanel(panel);
37508             if(this.config.initialSize && this.panels.getCount()==1){
37509                 this.resizeTo(this.config.initialSize);
37510             }
37511         }
37512         this.fireEvent("paneladded", this, panel);
37513         return panel;
37514     },
37515     
37516     /**
37517      * Returns true if the panel is in this region.
37518      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37519      * @return {Boolean}
37520      */
37521     hasPanel : function(panel){
37522         if(typeof panel == "object"){ // must be panel obj
37523             panel = panel.getId();
37524         }
37525         return this.getPanel(panel) ? true : false;
37526     },
37527     
37528     /**
37529      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37530      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37531      * @param {Boolean} preservePanel Overrides the config preservePanel option
37532      * @return {Roo.ContentPanel} The panel that was removed
37533      */
37534     remove : function(panel, preservePanel){
37535         panel = this.getPanel(panel);
37536         if(!panel){
37537             return null;
37538         }
37539         var e = {};
37540         this.fireEvent("beforeremove", this, panel, e);
37541         if(e.cancel === true){
37542             return null;
37543         }
37544         var panelId = panel.getId();
37545         this.panels.removeKey(panelId);
37546         return panel;
37547     },
37548     
37549     /**
37550      * Returns the panel specified or null if it's not in this region.
37551      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37552      * @return {Roo.ContentPanel}
37553      */
37554     getPanel : function(id){
37555         if(typeof id == "object"){ // must be panel obj
37556             return id;
37557         }
37558         return this.panels.get(id);
37559     },
37560     
37561     /**
37562      * Returns this regions position (north/south/east/west/center).
37563      * @return {String} 
37564      */
37565     getPosition: function(){
37566         return this.position;    
37567     }
37568 });/*
37569  * Based on:
37570  * Ext JS Library 1.1.1
37571  * Copyright(c) 2006-2007, Ext JS, LLC.
37572  *
37573  * Originally Released Under LGPL - original licence link has changed is not relivant.
37574  *
37575  * Fork - LGPL
37576  * <script type="text/javascript">
37577  */
37578  
37579 /**
37580  * @class Roo.bootstrap.layout.Region
37581  * @extends Roo.bootstrap.layout.Basic
37582  * This class represents a region in a layout manager.
37583  
37584  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37585  * @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})
37586  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
37587  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
37588  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
37589  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
37590  * @cfg {String}    title           The title for the region (overrides panel titles)
37591  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
37592  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37593  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
37594  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37595  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
37596  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37597  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
37598  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
37599  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
37600  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
37601
37602  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
37603  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
37604  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
37605  * @cfg {Number}    width           For East/West panels
37606  * @cfg {Number}    height          For North/South panels
37607  * @cfg {Boolean}   split           To show the splitter
37608  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
37609  * 
37610  * @cfg {string}   cls             Extra CSS classes to add to region
37611  * 
37612  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37613  * @cfg {string}   region  the region that it inhabits..
37614  *
37615
37616  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
37617  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
37618
37619  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
37620  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
37621  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
37622  */
37623 Roo.bootstrap.layout.Region = function(config)
37624 {
37625     this.applyConfig(config);
37626
37627     var mgr = config.mgr;
37628     var pos = config.region;
37629     config.skipConfig = true;
37630     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37631     
37632     if (mgr.el) {
37633         this.onRender(mgr.el);   
37634     }
37635      
37636     this.visible = true;
37637     this.collapsed = false;
37638     this.unrendered_panels = [];
37639 };
37640
37641 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37642
37643     position: '', // set by wrapper (eg. north/south etc..)
37644     unrendered_panels : null,  // unrendered panels.
37645     
37646     tabPosition : false,
37647     
37648     mgr: false, // points to 'Border'
37649     
37650     
37651     createBody : function(){
37652         /** This region's body element 
37653         * @type Roo.Element */
37654         this.bodyEl = this.el.createChild({
37655                 tag: "div",
37656                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37657         });
37658     },
37659
37660     onRender: function(ctr, pos)
37661     {
37662         var dh = Roo.DomHelper;
37663         /** This region's container element 
37664         * @type Roo.Element */
37665         this.el = dh.append(ctr.dom, {
37666                 tag: "div",
37667                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37668             }, true);
37669         /** This region's title element 
37670         * @type Roo.Element */
37671     
37672         this.titleEl = dh.append(this.el.dom,  {
37673                 tag: "div",
37674                 unselectable: "on",
37675                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37676                 children:[
37677                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
37678                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37679                 ]
37680             }, true);
37681         
37682         this.titleEl.enableDisplayMode();
37683         /** This region's title text element 
37684         * @type HTMLElement */
37685         this.titleTextEl = this.titleEl.dom.firstChild;
37686         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37687         /*
37688         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37689         this.closeBtn.enableDisplayMode();
37690         this.closeBtn.on("click", this.closeClicked, this);
37691         this.closeBtn.hide();
37692     */
37693         this.createBody(this.config);
37694         if(this.config.hideWhenEmpty){
37695             this.hide();
37696             this.on("paneladded", this.validateVisibility, this);
37697             this.on("panelremoved", this.validateVisibility, this);
37698         }
37699         if(this.autoScroll){
37700             this.bodyEl.setStyle("overflow", "auto");
37701         }else{
37702             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37703         }
37704         //if(c.titlebar !== false){
37705             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37706                 this.titleEl.hide();
37707             }else{
37708                 this.titleEl.show();
37709                 if(this.config.title){
37710                     this.titleTextEl.innerHTML = this.config.title;
37711                 }
37712             }
37713         //}
37714         if(this.config.collapsed){
37715             this.collapse(true);
37716         }
37717         if(this.config.hidden){
37718             this.hide();
37719         }
37720         
37721         if (this.unrendered_panels && this.unrendered_panels.length) {
37722             for (var i =0;i< this.unrendered_panels.length; i++) {
37723                 this.add(this.unrendered_panels[i]);
37724             }
37725             this.unrendered_panels = null;
37726             
37727         }
37728         
37729     },
37730     
37731     applyConfig : function(c)
37732     {
37733         /*
37734          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37735             var dh = Roo.DomHelper;
37736             if(c.titlebar !== false){
37737                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37738                 this.collapseBtn.on("click", this.collapse, this);
37739                 this.collapseBtn.enableDisplayMode();
37740                 /*
37741                 if(c.showPin === true || this.showPin){
37742                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37743                     this.stickBtn.enableDisplayMode();
37744                     this.stickBtn.on("click", this.expand, this);
37745                     this.stickBtn.hide();
37746                 }
37747                 
37748             }
37749             */
37750             /** This region's collapsed element
37751             * @type Roo.Element */
37752             /*
37753              *
37754             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37755                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37756             ]}, true);
37757             
37758             if(c.floatable !== false){
37759                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37760                this.collapsedEl.on("click", this.collapseClick, this);
37761             }
37762
37763             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37764                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37765                    id: "message", unselectable: "on", style:{"float":"left"}});
37766                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37767              }
37768             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37769             this.expandBtn.on("click", this.expand, this);
37770             
37771         }
37772         
37773         if(this.collapseBtn){
37774             this.collapseBtn.setVisible(c.collapsible == true);
37775         }
37776         
37777         this.cmargins = c.cmargins || this.cmargins ||
37778                          (this.position == "west" || this.position == "east" ?
37779                              {top: 0, left: 2, right:2, bottom: 0} :
37780                              {top: 2, left: 0, right:0, bottom: 2});
37781         */
37782         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37783         
37784         
37785         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
37786         
37787         this.autoScroll = c.autoScroll || false;
37788         
37789         
37790        
37791         
37792         this.duration = c.duration || .30;
37793         this.slideDuration = c.slideDuration || .45;
37794         this.config = c;
37795        
37796     },
37797     /**
37798      * Returns true if this region is currently visible.
37799      * @return {Boolean}
37800      */
37801     isVisible : function(){
37802         return this.visible;
37803     },
37804
37805     /**
37806      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
37807      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
37808      */
37809     //setCollapsedTitle : function(title){
37810     //    title = title || "&#160;";
37811      //   if(this.collapsedTitleTextEl){
37812       //      this.collapsedTitleTextEl.innerHTML = title;
37813        // }
37814     //},
37815
37816     getBox : function(){
37817         var b;
37818       //  if(!this.collapsed){
37819             b = this.el.getBox(false, true);
37820        // }else{
37821           //  b = this.collapsedEl.getBox(false, true);
37822         //}
37823         return b;
37824     },
37825
37826     getMargins : function(){
37827         return this.margins;
37828         //return this.collapsed ? this.cmargins : this.margins;
37829     },
37830 /*
37831     highlight : function(){
37832         this.el.addClass("x-layout-panel-dragover");
37833     },
37834
37835     unhighlight : function(){
37836         this.el.removeClass("x-layout-panel-dragover");
37837     },
37838 */
37839     updateBox : function(box)
37840     {
37841         if (!this.bodyEl) {
37842             return; // not rendered yet..
37843         }
37844         
37845         this.box = box;
37846         if(!this.collapsed){
37847             this.el.dom.style.left = box.x + "px";
37848             this.el.dom.style.top = box.y + "px";
37849             this.updateBody(box.width, box.height);
37850         }else{
37851             this.collapsedEl.dom.style.left = box.x + "px";
37852             this.collapsedEl.dom.style.top = box.y + "px";
37853             this.collapsedEl.setSize(box.width, box.height);
37854         }
37855         if(this.tabs){
37856             this.tabs.autoSizeTabs();
37857         }
37858     },
37859
37860     updateBody : function(w, h)
37861     {
37862         if(w !== null){
37863             this.el.setWidth(w);
37864             w -= this.el.getBorderWidth("rl");
37865             if(this.config.adjustments){
37866                 w += this.config.adjustments[0];
37867             }
37868         }
37869         if(h !== null && h > 0){
37870             this.el.setHeight(h);
37871             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
37872             h -= this.el.getBorderWidth("tb");
37873             if(this.config.adjustments){
37874                 h += this.config.adjustments[1];
37875             }
37876             this.bodyEl.setHeight(h);
37877             if(this.tabs){
37878                 h = this.tabs.syncHeight(h);
37879             }
37880         }
37881         if(this.panelSize){
37882             w = w !== null ? w : this.panelSize.width;
37883             h = h !== null ? h : this.panelSize.height;
37884         }
37885         if(this.activePanel){
37886             var el = this.activePanel.getEl();
37887             w = w !== null ? w : el.getWidth();
37888             h = h !== null ? h : el.getHeight();
37889             this.panelSize = {width: w, height: h};
37890             this.activePanel.setSize(w, h);
37891         }
37892         if(Roo.isIE && this.tabs){
37893             this.tabs.el.repaint();
37894         }
37895     },
37896
37897     /**
37898      * Returns the container element for this region.
37899      * @return {Roo.Element}
37900      */
37901     getEl : function(){
37902         return this.el;
37903     },
37904
37905     /**
37906      * Hides this region.
37907      */
37908     hide : function(){
37909         //if(!this.collapsed){
37910             this.el.dom.style.left = "-2000px";
37911             this.el.hide();
37912         //}else{
37913          //   this.collapsedEl.dom.style.left = "-2000px";
37914          //   this.collapsedEl.hide();
37915        // }
37916         this.visible = false;
37917         this.fireEvent("visibilitychange", this, false);
37918     },
37919
37920     /**
37921      * Shows this region if it was previously hidden.
37922      */
37923     show : function(){
37924         //if(!this.collapsed){
37925             this.el.show();
37926         //}else{
37927         //    this.collapsedEl.show();
37928        // }
37929         this.visible = true;
37930         this.fireEvent("visibilitychange", this, true);
37931     },
37932 /*
37933     closeClicked : function(){
37934         if(this.activePanel){
37935             this.remove(this.activePanel);
37936         }
37937     },
37938
37939     collapseClick : function(e){
37940         if(this.isSlid){
37941            e.stopPropagation();
37942            this.slideIn();
37943         }else{
37944            e.stopPropagation();
37945            this.slideOut();
37946         }
37947     },
37948 */
37949     /**
37950      * Collapses this region.
37951      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
37952      */
37953     /*
37954     collapse : function(skipAnim, skipCheck = false){
37955         if(this.collapsed) {
37956             return;
37957         }
37958         
37959         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
37960             
37961             this.collapsed = true;
37962             if(this.split){
37963                 this.split.el.hide();
37964             }
37965             if(this.config.animate && skipAnim !== true){
37966                 this.fireEvent("invalidated", this);
37967                 this.animateCollapse();
37968             }else{
37969                 this.el.setLocation(-20000,-20000);
37970                 this.el.hide();
37971                 this.collapsedEl.show();
37972                 this.fireEvent("collapsed", this);
37973                 this.fireEvent("invalidated", this);
37974             }
37975         }
37976         
37977     },
37978 */
37979     animateCollapse : function(){
37980         // overridden
37981     },
37982
37983     /**
37984      * Expands this region if it was previously collapsed.
37985      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
37986      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
37987      */
37988     /*
37989     expand : function(e, skipAnim){
37990         if(e) {
37991             e.stopPropagation();
37992         }
37993         if(!this.collapsed || this.el.hasActiveFx()) {
37994             return;
37995         }
37996         if(this.isSlid){
37997             this.afterSlideIn();
37998             skipAnim = true;
37999         }
38000         this.collapsed = false;
38001         if(this.config.animate && skipAnim !== true){
38002             this.animateExpand();
38003         }else{
38004             this.el.show();
38005             if(this.split){
38006                 this.split.el.show();
38007             }
38008             this.collapsedEl.setLocation(-2000,-2000);
38009             this.collapsedEl.hide();
38010             this.fireEvent("invalidated", this);
38011             this.fireEvent("expanded", this);
38012         }
38013     },
38014 */
38015     animateExpand : function(){
38016         // overridden
38017     },
38018
38019     initTabs : function()
38020     {
38021         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38022         
38023         var ts = new Roo.bootstrap.panel.Tabs({
38024             el: this.bodyEl.dom,
38025             region : this,
38026             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38027             disableTooltips: this.config.disableTabTips,
38028             toolbar : this.config.toolbar
38029         });
38030         
38031         if(this.config.hideTabs){
38032             ts.stripWrap.setDisplayed(false);
38033         }
38034         this.tabs = ts;
38035         ts.resizeTabs = this.config.resizeTabs === true;
38036         ts.minTabWidth = this.config.minTabWidth || 40;
38037         ts.maxTabWidth = this.config.maxTabWidth || 250;
38038         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38039         ts.monitorResize = false;
38040         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38041         ts.bodyEl.addClass('roo-layout-tabs-body');
38042         this.panels.each(this.initPanelAsTab, this);
38043     },
38044
38045     initPanelAsTab : function(panel){
38046         var ti = this.tabs.addTab(
38047             panel.getEl().id,
38048             panel.getTitle(),
38049             null,
38050             this.config.closeOnTab && panel.isClosable(),
38051             panel.tpl
38052         );
38053         if(panel.tabTip !== undefined){
38054             ti.setTooltip(panel.tabTip);
38055         }
38056         ti.on("activate", function(){
38057               this.setActivePanel(panel);
38058         }, this);
38059         
38060         if(this.config.closeOnTab){
38061             ti.on("beforeclose", function(t, e){
38062                 e.cancel = true;
38063                 this.remove(panel);
38064             }, this);
38065         }
38066         
38067         panel.tabItem = ti;
38068         
38069         return ti;
38070     },
38071
38072     updatePanelTitle : function(panel, title)
38073     {
38074         if(this.activePanel == panel){
38075             this.updateTitle(title);
38076         }
38077         if(this.tabs){
38078             var ti = this.tabs.getTab(panel.getEl().id);
38079             ti.setText(title);
38080             if(panel.tabTip !== undefined){
38081                 ti.setTooltip(panel.tabTip);
38082             }
38083         }
38084     },
38085
38086     updateTitle : function(title){
38087         if(this.titleTextEl && !this.config.title){
38088             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38089         }
38090     },
38091
38092     setActivePanel : function(panel)
38093     {
38094         panel = this.getPanel(panel);
38095         if(this.activePanel && this.activePanel != panel){
38096             if(this.activePanel.setActiveState(false) === false){
38097                 return;
38098             }
38099         }
38100         this.activePanel = panel;
38101         panel.setActiveState(true);
38102         if(this.panelSize){
38103             panel.setSize(this.panelSize.width, this.panelSize.height);
38104         }
38105         if(this.closeBtn){
38106             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38107         }
38108         this.updateTitle(panel.getTitle());
38109         if(this.tabs){
38110             this.fireEvent("invalidated", this);
38111         }
38112         this.fireEvent("panelactivated", this, panel);
38113     },
38114
38115     /**
38116      * Shows the specified panel.
38117      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38118      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38119      */
38120     showPanel : function(panel)
38121     {
38122         panel = this.getPanel(panel);
38123         if(panel){
38124             if(this.tabs){
38125                 var tab = this.tabs.getTab(panel.getEl().id);
38126                 if(tab.isHidden()){
38127                     this.tabs.unhideTab(tab.id);
38128                 }
38129                 tab.activate();
38130             }else{
38131                 this.setActivePanel(panel);
38132             }
38133         }
38134         return panel;
38135     },
38136
38137     /**
38138      * Get the active panel for this region.
38139      * @return {Roo.ContentPanel} The active panel or null
38140      */
38141     getActivePanel : function(){
38142         return this.activePanel;
38143     },
38144
38145     validateVisibility : function(){
38146         if(this.panels.getCount() < 1){
38147             this.updateTitle("&#160;");
38148             this.closeBtn.hide();
38149             this.hide();
38150         }else{
38151             if(!this.isVisible()){
38152                 this.show();
38153             }
38154         }
38155     },
38156
38157     /**
38158      * Adds the passed ContentPanel(s) to this region.
38159      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38160      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38161      */
38162     add : function(panel)
38163     {
38164         if(arguments.length > 1){
38165             for(var i = 0, len = arguments.length; i < len; i++) {
38166                 this.add(arguments[i]);
38167             }
38168             return null;
38169         }
38170         
38171         // if we have not been rendered yet, then we can not really do much of this..
38172         if (!this.bodyEl) {
38173             this.unrendered_panels.push(panel);
38174             return panel;
38175         }
38176         
38177         
38178         
38179         
38180         if(this.hasPanel(panel)){
38181             this.showPanel(panel);
38182             return panel;
38183         }
38184         panel.setRegion(this);
38185         this.panels.add(panel);
38186        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38187             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38188             // and hide them... ???
38189             this.bodyEl.dom.appendChild(panel.getEl().dom);
38190             if(panel.background !== true){
38191                 this.setActivePanel(panel);
38192             }
38193             this.fireEvent("paneladded", this, panel);
38194             return panel;
38195         }
38196         */
38197         if(!this.tabs){
38198             this.initTabs();
38199         }else{
38200             this.initPanelAsTab(panel);
38201         }
38202         
38203         
38204         if(panel.background !== true){
38205             this.tabs.activate(panel.getEl().id);
38206         }
38207         this.fireEvent("paneladded", this, panel);
38208         return panel;
38209     },
38210
38211     /**
38212      * Hides the tab for the specified panel.
38213      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38214      */
38215     hidePanel : function(panel){
38216         if(this.tabs && (panel = this.getPanel(panel))){
38217             this.tabs.hideTab(panel.getEl().id);
38218         }
38219     },
38220
38221     /**
38222      * Unhides the tab for a previously hidden panel.
38223      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38224      */
38225     unhidePanel : function(panel){
38226         if(this.tabs && (panel = this.getPanel(panel))){
38227             this.tabs.unhideTab(panel.getEl().id);
38228         }
38229     },
38230
38231     clearPanels : function(){
38232         while(this.panels.getCount() > 0){
38233              this.remove(this.panels.first());
38234         }
38235     },
38236
38237     /**
38238      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38239      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38240      * @param {Boolean} preservePanel Overrides the config preservePanel option
38241      * @return {Roo.ContentPanel} The panel that was removed
38242      */
38243     remove : function(panel, preservePanel)
38244     {
38245         panel = this.getPanel(panel);
38246         if(!panel){
38247             return null;
38248         }
38249         var e = {};
38250         this.fireEvent("beforeremove", this, panel, e);
38251         if(e.cancel === true){
38252             return null;
38253         }
38254         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38255         var panelId = panel.getId();
38256         this.panels.removeKey(panelId);
38257         if(preservePanel){
38258             document.body.appendChild(panel.getEl().dom);
38259         }
38260         if(this.tabs){
38261             this.tabs.removeTab(panel.getEl().id);
38262         }else if (!preservePanel){
38263             this.bodyEl.dom.removeChild(panel.getEl().dom);
38264         }
38265         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38266             var p = this.panels.first();
38267             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38268             tempEl.appendChild(p.getEl().dom);
38269             this.bodyEl.update("");
38270             this.bodyEl.dom.appendChild(p.getEl().dom);
38271             tempEl = null;
38272             this.updateTitle(p.getTitle());
38273             this.tabs = null;
38274             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38275             this.setActivePanel(p);
38276         }
38277         panel.setRegion(null);
38278         if(this.activePanel == panel){
38279             this.activePanel = null;
38280         }
38281         if(this.config.autoDestroy !== false && preservePanel !== true){
38282             try{panel.destroy();}catch(e){}
38283         }
38284         this.fireEvent("panelremoved", this, panel);
38285         return panel;
38286     },
38287
38288     /**
38289      * Returns the TabPanel component used by this region
38290      * @return {Roo.TabPanel}
38291      */
38292     getTabs : function(){
38293         return this.tabs;
38294     },
38295
38296     createTool : function(parentEl, className){
38297         var btn = Roo.DomHelper.append(parentEl, {
38298             tag: "div",
38299             cls: "x-layout-tools-button",
38300             children: [ {
38301                 tag: "div",
38302                 cls: "roo-layout-tools-button-inner " + className,
38303                 html: "&#160;"
38304             }]
38305         }, true);
38306         btn.addClassOnOver("roo-layout-tools-button-over");
38307         return btn;
38308     }
38309 });/*
38310  * Based on:
38311  * Ext JS Library 1.1.1
38312  * Copyright(c) 2006-2007, Ext JS, LLC.
38313  *
38314  * Originally Released Under LGPL - original licence link has changed is not relivant.
38315  *
38316  * Fork - LGPL
38317  * <script type="text/javascript">
38318  */
38319  
38320
38321
38322 /**
38323  * @class Roo.SplitLayoutRegion
38324  * @extends Roo.LayoutRegion
38325  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38326  */
38327 Roo.bootstrap.layout.Split = function(config){
38328     this.cursor = config.cursor;
38329     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38330 };
38331
38332 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38333 {
38334     splitTip : "Drag to resize.",
38335     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38336     useSplitTips : false,
38337
38338     applyConfig : function(config){
38339         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38340     },
38341     
38342     onRender : function(ctr,pos) {
38343         
38344         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38345         if(!this.config.split){
38346             return;
38347         }
38348         if(!this.split){
38349             
38350             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38351                             tag: "div",
38352                             id: this.el.id + "-split",
38353                             cls: "roo-layout-split roo-layout-split-"+this.position,
38354                             html: "&#160;"
38355             });
38356             /** The SplitBar for this region 
38357             * @type Roo.SplitBar */
38358             // does not exist yet...
38359             Roo.log([this.position, this.orientation]);
38360             
38361             this.split = new Roo.bootstrap.SplitBar({
38362                 dragElement : splitEl,
38363                 resizingElement: this.el,
38364                 orientation : this.orientation
38365             });
38366             
38367             this.split.on("moved", this.onSplitMove, this);
38368             this.split.useShim = this.config.useShim === true;
38369             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38370             if(this.useSplitTips){
38371                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38372             }
38373             //if(config.collapsible){
38374             //    this.split.el.on("dblclick", this.collapse,  this);
38375             //}
38376         }
38377         if(typeof this.config.minSize != "undefined"){
38378             this.split.minSize = this.config.minSize;
38379         }
38380         if(typeof this.config.maxSize != "undefined"){
38381             this.split.maxSize = this.config.maxSize;
38382         }
38383         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38384             this.hideSplitter();
38385         }
38386         
38387     },
38388
38389     getHMaxSize : function(){
38390          var cmax = this.config.maxSize || 10000;
38391          var center = this.mgr.getRegion("center");
38392          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38393     },
38394
38395     getVMaxSize : function(){
38396          var cmax = this.config.maxSize || 10000;
38397          var center = this.mgr.getRegion("center");
38398          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38399     },
38400
38401     onSplitMove : function(split, newSize){
38402         this.fireEvent("resized", this, newSize);
38403     },
38404     
38405     /** 
38406      * Returns the {@link Roo.SplitBar} for this region.
38407      * @return {Roo.SplitBar}
38408      */
38409     getSplitBar : function(){
38410         return this.split;
38411     },
38412     
38413     hide : function(){
38414         this.hideSplitter();
38415         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38416     },
38417
38418     hideSplitter : function(){
38419         if(this.split){
38420             this.split.el.setLocation(-2000,-2000);
38421             this.split.el.hide();
38422         }
38423     },
38424
38425     show : function(){
38426         if(this.split){
38427             this.split.el.show();
38428         }
38429         Roo.bootstrap.layout.Split.superclass.show.call(this);
38430     },
38431     
38432     beforeSlide: function(){
38433         if(Roo.isGecko){// firefox overflow auto bug workaround
38434             this.bodyEl.clip();
38435             if(this.tabs) {
38436                 this.tabs.bodyEl.clip();
38437             }
38438             if(this.activePanel){
38439                 this.activePanel.getEl().clip();
38440                 
38441                 if(this.activePanel.beforeSlide){
38442                     this.activePanel.beforeSlide();
38443                 }
38444             }
38445         }
38446     },
38447     
38448     afterSlide : function(){
38449         if(Roo.isGecko){// firefox overflow auto bug workaround
38450             this.bodyEl.unclip();
38451             if(this.tabs) {
38452                 this.tabs.bodyEl.unclip();
38453             }
38454             if(this.activePanel){
38455                 this.activePanel.getEl().unclip();
38456                 if(this.activePanel.afterSlide){
38457                     this.activePanel.afterSlide();
38458                 }
38459             }
38460         }
38461     },
38462
38463     initAutoHide : function(){
38464         if(this.autoHide !== false){
38465             if(!this.autoHideHd){
38466                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38467                 this.autoHideHd = {
38468                     "mouseout": function(e){
38469                         if(!e.within(this.el, true)){
38470                             st.delay(500);
38471                         }
38472                     },
38473                     "mouseover" : function(e){
38474                         st.cancel();
38475                     },
38476                     scope : this
38477                 };
38478             }
38479             this.el.on(this.autoHideHd);
38480         }
38481     },
38482
38483     clearAutoHide : function(){
38484         if(this.autoHide !== false){
38485             this.el.un("mouseout", this.autoHideHd.mouseout);
38486             this.el.un("mouseover", this.autoHideHd.mouseover);
38487         }
38488     },
38489
38490     clearMonitor : function(){
38491         Roo.get(document).un("click", this.slideInIf, this);
38492     },
38493
38494     // these names are backwards but not changed for compat
38495     slideOut : function(){
38496         if(this.isSlid || this.el.hasActiveFx()){
38497             return;
38498         }
38499         this.isSlid = true;
38500         if(this.collapseBtn){
38501             this.collapseBtn.hide();
38502         }
38503         this.closeBtnState = this.closeBtn.getStyle('display');
38504         this.closeBtn.hide();
38505         if(this.stickBtn){
38506             this.stickBtn.show();
38507         }
38508         this.el.show();
38509         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38510         this.beforeSlide();
38511         this.el.setStyle("z-index", 10001);
38512         this.el.slideIn(this.getSlideAnchor(), {
38513             callback: function(){
38514                 this.afterSlide();
38515                 this.initAutoHide();
38516                 Roo.get(document).on("click", this.slideInIf, this);
38517                 this.fireEvent("slideshow", this);
38518             },
38519             scope: this,
38520             block: true
38521         });
38522     },
38523
38524     afterSlideIn : function(){
38525         this.clearAutoHide();
38526         this.isSlid = false;
38527         this.clearMonitor();
38528         this.el.setStyle("z-index", "");
38529         if(this.collapseBtn){
38530             this.collapseBtn.show();
38531         }
38532         this.closeBtn.setStyle('display', this.closeBtnState);
38533         if(this.stickBtn){
38534             this.stickBtn.hide();
38535         }
38536         this.fireEvent("slidehide", this);
38537     },
38538
38539     slideIn : function(cb){
38540         if(!this.isSlid || this.el.hasActiveFx()){
38541             Roo.callback(cb);
38542             return;
38543         }
38544         this.isSlid = false;
38545         this.beforeSlide();
38546         this.el.slideOut(this.getSlideAnchor(), {
38547             callback: function(){
38548                 this.el.setLeftTop(-10000, -10000);
38549                 this.afterSlide();
38550                 this.afterSlideIn();
38551                 Roo.callback(cb);
38552             },
38553             scope: this,
38554             block: true
38555         });
38556     },
38557     
38558     slideInIf : function(e){
38559         if(!e.within(this.el)){
38560             this.slideIn();
38561         }
38562     },
38563
38564     animateCollapse : function(){
38565         this.beforeSlide();
38566         this.el.setStyle("z-index", 20000);
38567         var anchor = this.getSlideAnchor();
38568         this.el.slideOut(anchor, {
38569             callback : function(){
38570                 this.el.setStyle("z-index", "");
38571                 this.collapsedEl.slideIn(anchor, {duration:.3});
38572                 this.afterSlide();
38573                 this.el.setLocation(-10000,-10000);
38574                 this.el.hide();
38575                 this.fireEvent("collapsed", this);
38576             },
38577             scope: this,
38578             block: true
38579         });
38580     },
38581
38582     animateExpand : function(){
38583         this.beforeSlide();
38584         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38585         this.el.setStyle("z-index", 20000);
38586         this.collapsedEl.hide({
38587             duration:.1
38588         });
38589         this.el.slideIn(this.getSlideAnchor(), {
38590             callback : function(){
38591                 this.el.setStyle("z-index", "");
38592                 this.afterSlide();
38593                 if(this.split){
38594                     this.split.el.show();
38595                 }
38596                 this.fireEvent("invalidated", this);
38597                 this.fireEvent("expanded", this);
38598             },
38599             scope: this,
38600             block: true
38601         });
38602     },
38603
38604     anchors : {
38605         "west" : "left",
38606         "east" : "right",
38607         "north" : "top",
38608         "south" : "bottom"
38609     },
38610
38611     sanchors : {
38612         "west" : "l",
38613         "east" : "r",
38614         "north" : "t",
38615         "south" : "b"
38616     },
38617
38618     canchors : {
38619         "west" : "tl-tr",
38620         "east" : "tr-tl",
38621         "north" : "tl-bl",
38622         "south" : "bl-tl"
38623     },
38624
38625     getAnchor : function(){
38626         return this.anchors[this.position];
38627     },
38628
38629     getCollapseAnchor : function(){
38630         return this.canchors[this.position];
38631     },
38632
38633     getSlideAnchor : function(){
38634         return this.sanchors[this.position];
38635     },
38636
38637     getAlignAdj : function(){
38638         var cm = this.cmargins;
38639         switch(this.position){
38640             case "west":
38641                 return [0, 0];
38642             break;
38643             case "east":
38644                 return [0, 0];
38645             break;
38646             case "north":
38647                 return [0, 0];
38648             break;
38649             case "south":
38650                 return [0, 0];
38651             break;
38652         }
38653     },
38654
38655     getExpandAdj : function(){
38656         var c = this.collapsedEl, cm = this.cmargins;
38657         switch(this.position){
38658             case "west":
38659                 return [-(cm.right+c.getWidth()+cm.left), 0];
38660             break;
38661             case "east":
38662                 return [cm.right+c.getWidth()+cm.left, 0];
38663             break;
38664             case "north":
38665                 return [0, -(cm.top+cm.bottom+c.getHeight())];
38666             break;
38667             case "south":
38668                 return [0, cm.top+cm.bottom+c.getHeight()];
38669             break;
38670         }
38671     }
38672 });/*
38673  * Based on:
38674  * Ext JS Library 1.1.1
38675  * Copyright(c) 2006-2007, Ext JS, LLC.
38676  *
38677  * Originally Released Under LGPL - original licence link has changed is not relivant.
38678  *
38679  * Fork - LGPL
38680  * <script type="text/javascript">
38681  */
38682 /*
38683  * These classes are private internal classes
38684  */
38685 Roo.bootstrap.layout.Center = function(config){
38686     config.region = "center";
38687     Roo.bootstrap.layout.Region.call(this, config);
38688     this.visible = true;
38689     this.minWidth = config.minWidth || 20;
38690     this.minHeight = config.minHeight || 20;
38691 };
38692
38693 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38694     hide : function(){
38695         // center panel can't be hidden
38696     },
38697     
38698     show : function(){
38699         // center panel can't be hidden
38700     },
38701     
38702     getMinWidth: function(){
38703         return this.minWidth;
38704     },
38705     
38706     getMinHeight: function(){
38707         return this.minHeight;
38708     }
38709 });
38710
38711
38712
38713
38714  
38715
38716
38717
38718
38719
38720
38721 Roo.bootstrap.layout.North = function(config)
38722 {
38723     config.region = 'north';
38724     config.cursor = 'n-resize';
38725     
38726     Roo.bootstrap.layout.Split.call(this, config);
38727     
38728     
38729     if(this.split){
38730         this.split.placement = Roo.bootstrap.SplitBar.TOP;
38731         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38732         this.split.el.addClass("roo-layout-split-v");
38733     }
38734     var size = config.initialSize || config.height;
38735     if(typeof size != "undefined"){
38736         this.el.setHeight(size);
38737     }
38738 };
38739 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38740 {
38741     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38742     
38743     
38744     
38745     getBox : function(){
38746         if(this.collapsed){
38747             return this.collapsedEl.getBox();
38748         }
38749         var box = this.el.getBox();
38750         if(this.split){
38751             box.height += this.split.el.getHeight();
38752         }
38753         return box;
38754     },
38755     
38756     updateBox : function(box){
38757         if(this.split && !this.collapsed){
38758             box.height -= this.split.el.getHeight();
38759             this.split.el.setLeft(box.x);
38760             this.split.el.setTop(box.y+box.height);
38761             this.split.el.setWidth(box.width);
38762         }
38763         if(this.collapsed){
38764             this.updateBody(box.width, null);
38765         }
38766         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38767     }
38768 });
38769
38770
38771
38772
38773
38774 Roo.bootstrap.layout.South = function(config){
38775     config.region = 'south';
38776     config.cursor = 's-resize';
38777     Roo.bootstrap.layout.Split.call(this, config);
38778     if(this.split){
38779         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38780         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38781         this.split.el.addClass("roo-layout-split-v");
38782     }
38783     var size = config.initialSize || config.height;
38784     if(typeof size != "undefined"){
38785         this.el.setHeight(size);
38786     }
38787 };
38788
38789 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
38790     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38791     getBox : function(){
38792         if(this.collapsed){
38793             return this.collapsedEl.getBox();
38794         }
38795         var box = this.el.getBox();
38796         if(this.split){
38797             var sh = this.split.el.getHeight();
38798             box.height += sh;
38799             box.y -= sh;
38800         }
38801         return box;
38802     },
38803     
38804     updateBox : function(box){
38805         if(this.split && !this.collapsed){
38806             var sh = this.split.el.getHeight();
38807             box.height -= sh;
38808             box.y += sh;
38809             this.split.el.setLeft(box.x);
38810             this.split.el.setTop(box.y-sh);
38811             this.split.el.setWidth(box.width);
38812         }
38813         if(this.collapsed){
38814             this.updateBody(box.width, null);
38815         }
38816         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38817     }
38818 });
38819
38820 Roo.bootstrap.layout.East = function(config){
38821     config.region = "east";
38822     config.cursor = "e-resize";
38823     Roo.bootstrap.layout.Split.call(this, config);
38824     if(this.split){
38825         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
38826         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38827         this.split.el.addClass("roo-layout-split-h");
38828     }
38829     var size = config.initialSize || config.width;
38830     if(typeof size != "undefined"){
38831         this.el.setWidth(size);
38832     }
38833 };
38834 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
38835     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38836     getBox : function(){
38837         if(this.collapsed){
38838             return this.collapsedEl.getBox();
38839         }
38840         var box = this.el.getBox();
38841         if(this.split){
38842             var sw = this.split.el.getWidth();
38843             box.width += sw;
38844             box.x -= sw;
38845         }
38846         return box;
38847     },
38848
38849     updateBox : function(box){
38850         if(this.split && !this.collapsed){
38851             var sw = this.split.el.getWidth();
38852             box.width -= sw;
38853             this.split.el.setLeft(box.x);
38854             this.split.el.setTop(box.y);
38855             this.split.el.setHeight(box.height);
38856             box.x += sw;
38857         }
38858         if(this.collapsed){
38859             this.updateBody(null, box.height);
38860         }
38861         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38862     }
38863 });
38864
38865 Roo.bootstrap.layout.West = function(config){
38866     config.region = "west";
38867     config.cursor = "w-resize";
38868     
38869     Roo.bootstrap.layout.Split.call(this, config);
38870     if(this.split){
38871         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
38872         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38873         this.split.el.addClass("roo-layout-split-h");
38874     }
38875     
38876 };
38877 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
38878     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38879     
38880     onRender: function(ctr, pos)
38881     {
38882         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
38883         var size = this.config.initialSize || this.config.width;
38884         if(typeof size != "undefined"){
38885             this.el.setWidth(size);
38886         }
38887     },
38888     
38889     getBox : function(){
38890         if(this.collapsed){
38891             return this.collapsedEl.getBox();
38892         }
38893         var box = this.el.getBox();
38894         if(this.split){
38895             box.width += this.split.el.getWidth();
38896         }
38897         return box;
38898     },
38899     
38900     updateBox : function(box){
38901         if(this.split && !this.collapsed){
38902             var sw = this.split.el.getWidth();
38903             box.width -= sw;
38904             this.split.el.setLeft(box.x+box.width);
38905             this.split.el.setTop(box.y);
38906             this.split.el.setHeight(box.height);
38907         }
38908         if(this.collapsed){
38909             this.updateBody(null, box.height);
38910         }
38911         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38912     }
38913 });Roo.namespace("Roo.bootstrap.panel");/*
38914  * Based on:
38915  * Ext JS Library 1.1.1
38916  * Copyright(c) 2006-2007, Ext JS, LLC.
38917  *
38918  * Originally Released Under LGPL - original licence link has changed is not relivant.
38919  *
38920  * Fork - LGPL
38921  * <script type="text/javascript">
38922  */
38923 /**
38924  * @class Roo.ContentPanel
38925  * @extends Roo.util.Observable
38926  * A basic ContentPanel element.
38927  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
38928  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
38929  * @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
38930  * @cfg {Boolean}   closable      True if the panel can be closed/removed
38931  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
38932  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
38933  * @cfg {Toolbar}   toolbar       A toolbar for this panel
38934  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
38935  * @cfg {String} title          The title for this panel
38936  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
38937  * @cfg {String} url            Calls {@link #setUrl} with this value
38938  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
38939  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
38940  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
38941  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
38942  * @cfg {Boolean} badges render the badges
38943
38944  * @constructor
38945  * Create a new ContentPanel.
38946  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
38947  * @param {String/Object} config A string to set only the title or a config object
38948  * @param {String} content (optional) Set the HTML content for this panel
38949  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
38950  */
38951 Roo.bootstrap.panel.Content = function( config){
38952     
38953     this.tpl = config.tpl || false;
38954     
38955     var el = config.el;
38956     var content = config.content;
38957
38958     if(config.autoCreate){ // xtype is available if this is called from factory
38959         el = Roo.id();
38960     }
38961     this.el = Roo.get(el);
38962     if(!this.el && config && config.autoCreate){
38963         if(typeof config.autoCreate == "object"){
38964             if(!config.autoCreate.id){
38965                 config.autoCreate.id = config.id||el;
38966             }
38967             this.el = Roo.DomHelper.append(document.body,
38968                         config.autoCreate, true);
38969         }else{
38970             var elcfg =  {   tag: "div",
38971                             cls: "roo-layout-inactive-content",
38972                             id: config.id||el
38973                             };
38974             if (config.html) {
38975                 elcfg.html = config.html;
38976                 
38977             }
38978                         
38979             this.el = Roo.DomHelper.append(document.body, elcfg , true);
38980         }
38981     } 
38982     this.closable = false;
38983     this.loaded = false;
38984     this.active = false;
38985    
38986       
38987     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
38988         
38989         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
38990         
38991         this.wrapEl = this.el; //this.el.wrap();
38992         var ti = [];
38993         if (config.toolbar.items) {
38994             ti = config.toolbar.items ;
38995             delete config.toolbar.items ;
38996         }
38997         
38998         var nitems = [];
38999         this.toolbar.render(this.wrapEl, 'before');
39000         for(var i =0;i < ti.length;i++) {
39001           //  Roo.log(['add child', items[i]]);
39002             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39003         }
39004         this.toolbar.items = nitems;
39005         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39006         delete config.toolbar;
39007         
39008     }
39009     /*
39010     // xtype created footer. - not sure if will work as we normally have to render first..
39011     if (this.footer && !this.footer.el && this.footer.xtype) {
39012         if (!this.wrapEl) {
39013             this.wrapEl = this.el.wrap();
39014         }
39015     
39016         this.footer.container = this.wrapEl.createChild();
39017          
39018         this.footer = Roo.factory(this.footer, Roo);
39019         
39020     }
39021     */
39022     
39023      if(typeof config == "string"){
39024         this.title = config;
39025     }else{
39026         Roo.apply(this, config);
39027     }
39028     
39029     if(this.resizeEl){
39030         this.resizeEl = Roo.get(this.resizeEl, true);
39031     }else{
39032         this.resizeEl = this.el;
39033     }
39034     // handle view.xtype
39035     
39036  
39037     
39038     
39039     this.addEvents({
39040         /**
39041          * @event activate
39042          * Fires when this panel is activated. 
39043          * @param {Roo.ContentPanel} this
39044          */
39045         "activate" : true,
39046         /**
39047          * @event deactivate
39048          * Fires when this panel is activated. 
39049          * @param {Roo.ContentPanel} this
39050          */
39051         "deactivate" : true,
39052
39053         /**
39054          * @event resize
39055          * Fires when this panel is resized if fitToFrame is true.
39056          * @param {Roo.ContentPanel} this
39057          * @param {Number} width The width after any component adjustments
39058          * @param {Number} height The height after any component adjustments
39059          */
39060         "resize" : true,
39061         
39062          /**
39063          * @event render
39064          * Fires when this tab is created
39065          * @param {Roo.ContentPanel} this
39066          */
39067         "render" : true
39068         
39069         
39070         
39071     });
39072     
39073
39074     
39075     
39076     if(this.autoScroll){
39077         this.resizeEl.setStyle("overflow", "auto");
39078     } else {
39079         // fix randome scrolling
39080         //this.el.on('scroll', function() {
39081         //    Roo.log('fix random scolling');
39082         //    this.scrollTo('top',0); 
39083         //});
39084     }
39085     content = content || this.content;
39086     if(content){
39087         this.setContent(content);
39088     }
39089     if(config && config.url){
39090         this.setUrl(this.url, this.params, this.loadOnce);
39091     }
39092     
39093     
39094     
39095     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39096     
39097     if (this.view && typeof(this.view.xtype) != 'undefined') {
39098         this.view.el = this.el.appendChild(document.createElement("div"));
39099         this.view = Roo.factory(this.view); 
39100         this.view.render  &&  this.view.render(false, '');  
39101     }
39102     
39103     
39104     this.fireEvent('render', this);
39105 };
39106
39107 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39108     
39109     tabTip : '',
39110     
39111     setRegion : function(region){
39112         this.region = region;
39113         this.setActiveClass(region && !this.background);
39114     },
39115     
39116     
39117     setActiveClass: function(state)
39118     {
39119         if(state){
39120            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39121            this.el.setStyle('position','relative');
39122         }else{
39123            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39124            this.el.setStyle('position', 'absolute');
39125         } 
39126     },
39127     
39128     /**
39129      * Returns the toolbar for this Panel if one was configured. 
39130      * @return {Roo.Toolbar} 
39131      */
39132     getToolbar : function(){
39133         return this.toolbar;
39134     },
39135     
39136     setActiveState : function(active)
39137     {
39138         this.active = active;
39139         this.setActiveClass(active);
39140         if(!active){
39141             if(this.fireEvent("deactivate", this) === false){
39142                 return false;
39143             }
39144             return true;
39145         }
39146         this.fireEvent("activate", this);
39147         return true;
39148     },
39149     /**
39150      * Updates this panel's element
39151      * @param {String} content The new content
39152      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39153     */
39154     setContent : function(content, loadScripts){
39155         this.el.update(content, loadScripts);
39156     },
39157
39158     ignoreResize : function(w, h){
39159         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39160             return true;
39161         }else{
39162             this.lastSize = {width: w, height: h};
39163             return false;
39164         }
39165     },
39166     /**
39167      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39168      * @return {Roo.UpdateManager} The UpdateManager
39169      */
39170     getUpdateManager : function(){
39171         return this.el.getUpdateManager();
39172     },
39173      /**
39174      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39175      * @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:
39176 <pre><code>
39177 panel.load({
39178     url: "your-url.php",
39179     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39180     callback: yourFunction,
39181     scope: yourObject, //(optional scope)
39182     discardUrl: false,
39183     nocache: false,
39184     text: "Loading...",
39185     timeout: 30,
39186     scripts: false
39187 });
39188 </code></pre>
39189      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39190      * 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.
39191      * @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}
39192      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39193      * @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.
39194      * @return {Roo.ContentPanel} this
39195      */
39196     load : function(){
39197         var um = this.el.getUpdateManager();
39198         um.update.apply(um, arguments);
39199         return this;
39200     },
39201
39202
39203     /**
39204      * 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.
39205      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39206      * @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)
39207      * @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)
39208      * @return {Roo.UpdateManager} The UpdateManager
39209      */
39210     setUrl : function(url, params, loadOnce){
39211         if(this.refreshDelegate){
39212             this.removeListener("activate", this.refreshDelegate);
39213         }
39214         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39215         this.on("activate", this.refreshDelegate);
39216         return this.el.getUpdateManager();
39217     },
39218     
39219     _handleRefresh : function(url, params, loadOnce){
39220         if(!loadOnce || !this.loaded){
39221             var updater = this.el.getUpdateManager();
39222             updater.update(url, params, this._setLoaded.createDelegate(this));
39223         }
39224     },
39225     
39226     _setLoaded : function(){
39227         this.loaded = true;
39228     }, 
39229     
39230     /**
39231      * Returns this panel's id
39232      * @return {String} 
39233      */
39234     getId : function(){
39235         return this.el.id;
39236     },
39237     
39238     /** 
39239      * Returns this panel's element - used by regiosn to add.
39240      * @return {Roo.Element} 
39241      */
39242     getEl : function(){
39243         return this.wrapEl || this.el;
39244     },
39245     
39246    
39247     
39248     adjustForComponents : function(width, height)
39249     {
39250         //Roo.log('adjustForComponents ');
39251         if(this.resizeEl != this.el){
39252             width -= this.el.getFrameWidth('lr');
39253             height -= this.el.getFrameWidth('tb');
39254         }
39255         if(this.toolbar){
39256             var te = this.toolbar.getEl();
39257             te.setWidth(width);
39258             height -= te.getHeight();
39259         }
39260         if(this.footer){
39261             var te = this.footer.getEl();
39262             te.setWidth(width);
39263             height -= te.getHeight();
39264         }
39265         
39266         
39267         if(this.adjustments){
39268             width += this.adjustments[0];
39269             height += this.adjustments[1];
39270         }
39271         return {"width": width, "height": height};
39272     },
39273     
39274     setSize : function(width, height){
39275         if(this.fitToFrame && !this.ignoreResize(width, height)){
39276             if(this.fitContainer && this.resizeEl != this.el){
39277                 this.el.setSize(width, height);
39278             }
39279             var size = this.adjustForComponents(width, height);
39280             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39281             this.fireEvent('resize', this, size.width, size.height);
39282         }
39283     },
39284     
39285     /**
39286      * Returns this panel's title
39287      * @return {String} 
39288      */
39289     getTitle : function(){
39290         
39291         if (typeof(this.title) != 'object') {
39292             return this.title;
39293         }
39294         
39295         var t = '';
39296         for (var k in this.title) {
39297             if (!this.title.hasOwnProperty(k)) {
39298                 continue;
39299             }
39300             
39301             if (k.indexOf('-') >= 0) {
39302                 var s = k.split('-');
39303                 for (var i = 0; i<s.length; i++) {
39304                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39305                 }
39306             } else {
39307                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39308             }
39309         }
39310         return t;
39311     },
39312     
39313     /**
39314      * Set this panel's title
39315      * @param {String} title
39316      */
39317     setTitle : function(title){
39318         this.title = title;
39319         if(this.region){
39320             this.region.updatePanelTitle(this, title);
39321         }
39322     },
39323     
39324     /**
39325      * Returns true is this panel was configured to be closable
39326      * @return {Boolean} 
39327      */
39328     isClosable : function(){
39329         return this.closable;
39330     },
39331     
39332     beforeSlide : function(){
39333         this.el.clip();
39334         this.resizeEl.clip();
39335     },
39336     
39337     afterSlide : function(){
39338         this.el.unclip();
39339         this.resizeEl.unclip();
39340     },
39341     
39342     /**
39343      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39344      *   Will fail silently if the {@link #setUrl} method has not been called.
39345      *   This does not activate the panel, just updates its content.
39346      */
39347     refresh : function(){
39348         if(this.refreshDelegate){
39349            this.loaded = false;
39350            this.refreshDelegate();
39351         }
39352     },
39353     
39354     /**
39355      * Destroys this panel
39356      */
39357     destroy : function(){
39358         this.el.removeAllListeners();
39359         var tempEl = document.createElement("span");
39360         tempEl.appendChild(this.el.dom);
39361         tempEl.innerHTML = "";
39362         this.el.remove();
39363         this.el = null;
39364     },
39365     
39366     /**
39367      * form - if the content panel contains a form - this is a reference to it.
39368      * @type {Roo.form.Form}
39369      */
39370     form : false,
39371     /**
39372      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39373      *    This contains a reference to it.
39374      * @type {Roo.View}
39375      */
39376     view : false,
39377     
39378       /**
39379      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39380      * <pre><code>
39381
39382 layout.addxtype({
39383        xtype : 'Form',
39384        items: [ .... ]
39385    }
39386 );
39387
39388 </code></pre>
39389      * @param {Object} cfg Xtype definition of item to add.
39390      */
39391     
39392     
39393     getChildContainer: function () {
39394         return this.getEl();
39395     }
39396     
39397     
39398     /*
39399         var  ret = new Roo.factory(cfg);
39400         return ret;
39401         
39402         
39403         // add form..
39404         if (cfg.xtype.match(/^Form$/)) {
39405             
39406             var el;
39407             //if (this.footer) {
39408             //    el = this.footer.container.insertSibling(false, 'before');
39409             //} else {
39410                 el = this.el.createChild();
39411             //}
39412
39413             this.form = new  Roo.form.Form(cfg);
39414             
39415             
39416             if ( this.form.allItems.length) {
39417                 this.form.render(el.dom);
39418             }
39419             return this.form;
39420         }
39421         // should only have one of theses..
39422         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39423             // views.. should not be just added - used named prop 'view''
39424             
39425             cfg.el = this.el.appendChild(document.createElement("div"));
39426             // factory?
39427             
39428             var ret = new Roo.factory(cfg);
39429              
39430              ret.render && ret.render(false, ''); // render blank..
39431             this.view = ret;
39432             return ret;
39433         }
39434         return false;
39435     }
39436     \*/
39437 });
39438  
39439 /**
39440  * @class Roo.bootstrap.panel.Grid
39441  * @extends Roo.bootstrap.panel.Content
39442  * @constructor
39443  * Create a new GridPanel.
39444  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39445  * @param {Object} config A the config object
39446   
39447  */
39448
39449
39450
39451 Roo.bootstrap.panel.Grid = function(config)
39452 {
39453     
39454       
39455     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39456         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39457
39458     config.el = this.wrapper;
39459     //this.el = this.wrapper;
39460     
39461       if (config.container) {
39462         // ctor'ed from a Border/panel.grid
39463         
39464         
39465         this.wrapper.setStyle("overflow", "hidden");
39466         this.wrapper.addClass('roo-grid-container');
39467
39468     }
39469     
39470     
39471     if(config.toolbar){
39472         var tool_el = this.wrapper.createChild();    
39473         this.toolbar = Roo.factory(config.toolbar);
39474         var ti = [];
39475         if (config.toolbar.items) {
39476             ti = config.toolbar.items ;
39477             delete config.toolbar.items ;
39478         }
39479         
39480         var nitems = [];
39481         this.toolbar.render(tool_el);
39482         for(var i =0;i < ti.length;i++) {
39483           //  Roo.log(['add child', items[i]]);
39484             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39485         }
39486         this.toolbar.items = nitems;
39487         
39488         delete config.toolbar;
39489     }
39490     
39491     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39492     config.grid.scrollBody = true;;
39493     config.grid.monitorWindowResize = false; // turn off autosizing
39494     config.grid.autoHeight = false;
39495     config.grid.autoWidth = false;
39496     
39497     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39498     
39499     if (config.background) {
39500         // render grid on panel activation (if panel background)
39501         this.on('activate', function(gp) {
39502             if (!gp.grid.rendered) {
39503                 gp.grid.render(this.wrapper);
39504                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
39505             }
39506         });
39507             
39508     } else {
39509         this.grid.render(this.wrapper);
39510         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
39511
39512     }
39513     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39514     // ??? needed ??? config.el = this.wrapper;
39515     
39516     
39517     
39518   
39519     // xtype created footer. - not sure if will work as we normally have to render first..
39520     if (this.footer && !this.footer.el && this.footer.xtype) {
39521         
39522         var ctr = this.grid.getView().getFooterPanel(true);
39523         this.footer.dataSource = this.grid.dataSource;
39524         this.footer = Roo.factory(this.footer, Roo);
39525         this.footer.render(ctr);
39526         
39527     }
39528     
39529     
39530     
39531     
39532      
39533 };
39534
39535 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39536     getId : function(){
39537         return this.grid.id;
39538     },
39539     
39540     /**
39541      * Returns the grid for this panel
39542      * @return {Roo.bootstrap.Table} 
39543      */
39544     getGrid : function(){
39545         return this.grid;    
39546     },
39547     
39548     setSize : function(width, height){
39549         if(!this.ignoreResize(width, height)){
39550             var grid = this.grid;
39551             var size = this.adjustForComponents(width, height);
39552             var gridel = grid.getGridEl();
39553             gridel.setSize(size.width, size.height);
39554             /*
39555             var thd = grid.getGridEl().select('thead',true).first();
39556             var tbd = grid.getGridEl().select('tbody', true).first();
39557             if (tbd) {
39558                 tbd.setSize(width, height - thd.getHeight());
39559             }
39560             */
39561             grid.autoSize();
39562         }
39563     },
39564      
39565     
39566     
39567     beforeSlide : function(){
39568         this.grid.getView().scroller.clip();
39569     },
39570     
39571     afterSlide : function(){
39572         this.grid.getView().scroller.unclip();
39573     },
39574     
39575     destroy : function(){
39576         this.grid.destroy();
39577         delete this.grid;
39578         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
39579     }
39580 });
39581
39582 /**
39583  * @class Roo.bootstrap.panel.Nest
39584  * @extends Roo.bootstrap.panel.Content
39585  * @constructor
39586  * Create a new Panel, that can contain a layout.Border.
39587  * 
39588  * 
39589  * @param {Roo.BorderLayout} layout The layout for this panel
39590  * @param {String/Object} config A string to set only the title or a config object
39591  */
39592 Roo.bootstrap.panel.Nest = function(config)
39593 {
39594     // construct with only one argument..
39595     /* FIXME - implement nicer consturctors
39596     if (layout.layout) {
39597         config = layout;
39598         layout = config.layout;
39599         delete config.layout;
39600     }
39601     if (layout.xtype && !layout.getEl) {
39602         // then layout needs constructing..
39603         layout = Roo.factory(layout, Roo);
39604     }
39605     */
39606     
39607     config.el =  config.layout.getEl();
39608     
39609     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39610     
39611     config.layout.monitorWindowResize = false; // turn off autosizing
39612     this.layout = config.layout;
39613     this.layout.getEl().addClass("roo-layout-nested-layout");
39614     this.layout.parent = this;
39615     
39616     
39617     
39618     
39619 };
39620
39621 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39622
39623     setSize : function(width, height){
39624         if(!this.ignoreResize(width, height)){
39625             var size = this.adjustForComponents(width, height);
39626             var el = this.layout.getEl();
39627             if (size.height < 1) {
39628                 el.setWidth(size.width);   
39629             } else {
39630                 el.setSize(size.width, size.height);
39631             }
39632             var touch = el.dom.offsetWidth;
39633             this.layout.layout();
39634             // ie requires a double layout on the first pass
39635             if(Roo.isIE && !this.initialized){
39636                 this.initialized = true;
39637                 this.layout.layout();
39638             }
39639         }
39640     },
39641     
39642     // activate all subpanels if not currently active..
39643     
39644     setActiveState : function(active){
39645         this.active = active;
39646         this.setActiveClass(active);
39647         
39648         if(!active){
39649             this.fireEvent("deactivate", this);
39650             return;
39651         }
39652         
39653         this.fireEvent("activate", this);
39654         // not sure if this should happen before or after..
39655         if (!this.layout) {
39656             return; // should not happen..
39657         }
39658         var reg = false;
39659         for (var r in this.layout.regions) {
39660             reg = this.layout.getRegion(r);
39661             if (reg.getActivePanel()) {
39662                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
39663                 reg.setActivePanel(reg.getActivePanel());
39664                 continue;
39665             }
39666             if (!reg.panels.length) {
39667                 continue;
39668             }
39669             reg.showPanel(reg.getPanel(0));
39670         }
39671         
39672         
39673         
39674         
39675     },
39676     
39677     /**
39678      * Returns the nested BorderLayout for this panel
39679      * @return {Roo.BorderLayout} 
39680      */
39681     getLayout : function(){
39682         return this.layout;
39683     },
39684     
39685      /**
39686      * Adds a xtype elements to the layout of the nested panel
39687      * <pre><code>
39688
39689 panel.addxtype({
39690        xtype : 'ContentPanel',
39691        region: 'west',
39692        items: [ .... ]
39693    }
39694 );
39695
39696 panel.addxtype({
39697         xtype : 'NestedLayoutPanel',
39698         region: 'west',
39699         layout: {
39700            center: { },
39701            west: { }   
39702         },
39703         items : [ ... list of content panels or nested layout panels.. ]
39704    }
39705 );
39706 </code></pre>
39707      * @param {Object} cfg Xtype definition of item to add.
39708      */
39709     addxtype : function(cfg) {
39710         return this.layout.addxtype(cfg);
39711     
39712     }
39713 });/*
39714  * Based on:
39715  * Ext JS Library 1.1.1
39716  * Copyright(c) 2006-2007, Ext JS, LLC.
39717  *
39718  * Originally Released Under LGPL - original licence link has changed is not relivant.
39719  *
39720  * Fork - LGPL
39721  * <script type="text/javascript">
39722  */
39723 /**
39724  * @class Roo.TabPanel
39725  * @extends Roo.util.Observable
39726  * A lightweight tab container.
39727  * <br><br>
39728  * Usage:
39729  * <pre><code>
39730 // basic tabs 1, built from existing content
39731 var tabs = new Roo.TabPanel("tabs1");
39732 tabs.addTab("script", "View Script");
39733 tabs.addTab("markup", "View Markup");
39734 tabs.activate("script");
39735
39736 // more advanced tabs, built from javascript
39737 var jtabs = new Roo.TabPanel("jtabs");
39738 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39739
39740 // set up the UpdateManager
39741 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39742 var updater = tab2.getUpdateManager();
39743 updater.setDefaultUrl("ajax1.htm");
39744 tab2.on('activate', updater.refresh, updater, true);
39745
39746 // Use setUrl for Ajax loading
39747 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39748 tab3.setUrl("ajax2.htm", null, true);
39749
39750 // Disabled tab
39751 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39752 tab4.disable();
39753
39754 jtabs.activate("jtabs-1");
39755  * </code></pre>
39756  * @constructor
39757  * Create a new TabPanel.
39758  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39759  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39760  */
39761 Roo.bootstrap.panel.Tabs = function(config){
39762     /**
39763     * The container element for this TabPanel.
39764     * @type Roo.Element
39765     */
39766     this.el = Roo.get(config.el);
39767     delete config.el;
39768     if(config){
39769         if(typeof config == "boolean"){
39770             this.tabPosition = config ? "bottom" : "top";
39771         }else{
39772             Roo.apply(this, config);
39773         }
39774     }
39775     
39776     if(this.tabPosition == "bottom"){
39777         // if tabs are at the bottom = create the body first.
39778         this.bodyEl = Roo.get(this.createBody(this.el.dom));
39779         this.el.addClass("roo-tabs-bottom");
39780     }
39781     // next create the tabs holders
39782     
39783     if (this.tabPosition == "west"){
39784         
39785         var reg = this.region; // fake it..
39786         while (reg) {
39787             if (!reg.mgr.parent) {
39788                 break;
39789             }
39790             reg = reg.mgr.parent.region;
39791         }
39792         Roo.log("got nest?");
39793         Roo.log(reg);
39794         if (reg.mgr.getRegion('west')) {
39795             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
39796             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
39797             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39798             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39799             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39800         
39801             
39802         }
39803         
39804         
39805     } else {
39806      
39807         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
39808         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39809         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39810         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39811     }
39812     
39813     
39814     if(Roo.isIE){
39815         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
39816     }
39817     
39818     // finally - if tabs are at the top, then create the body last..
39819     if(this.tabPosition != "bottom"){
39820         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
39821          * @type Roo.Element
39822          */
39823         this.bodyEl = Roo.get(this.createBody(this.el.dom));
39824         this.el.addClass("roo-tabs-top");
39825     }
39826     this.items = [];
39827
39828     this.bodyEl.setStyle("position", "relative");
39829
39830     this.active = null;
39831     this.activateDelegate = this.activate.createDelegate(this);
39832
39833     this.addEvents({
39834         /**
39835          * @event tabchange
39836          * Fires when the active tab changes
39837          * @param {Roo.TabPanel} this
39838          * @param {Roo.TabPanelItem} activePanel The new active tab
39839          */
39840         "tabchange": true,
39841         /**
39842          * @event beforetabchange
39843          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
39844          * @param {Roo.TabPanel} this
39845          * @param {Object} e Set cancel to true on this object to cancel the tab change
39846          * @param {Roo.TabPanelItem} tab The tab being changed to
39847          */
39848         "beforetabchange" : true
39849     });
39850
39851     Roo.EventManager.onWindowResize(this.onResize, this);
39852     this.cpad = this.el.getPadding("lr");
39853     this.hiddenCount = 0;
39854
39855
39856     // toolbar on the tabbar support...
39857     if (this.toolbar) {
39858         alert("no toolbar support yet");
39859         this.toolbar  = false;
39860         /*
39861         var tcfg = this.toolbar;
39862         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
39863         this.toolbar = new Roo.Toolbar(tcfg);
39864         if (Roo.isSafari) {
39865             var tbl = tcfg.container.child('table', true);
39866             tbl.setAttribute('width', '100%');
39867         }
39868         */
39869         
39870     }
39871    
39872
39873
39874     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
39875 };
39876
39877 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
39878     /*
39879      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
39880      */
39881     tabPosition : "top",
39882     /*
39883      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
39884      */
39885     currentTabWidth : 0,
39886     /*
39887      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
39888      */
39889     minTabWidth : 40,
39890     /*
39891      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
39892      */
39893     maxTabWidth : 250,
39894     /*
39895      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
39896      */
39897     preferredTabWidth : 175,
39898     /*
39899      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
39900      */
39901     resizeTabs : false,
39902     /*
39903      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
39904      */
39905     monitorResize : true,
39906     /*
39907      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
39908      */
39909     toolbar : false,  // set by caller..
39910     
39911     region : false, /// set by caller
39912     
39913     disableTooltips : true, // not used yet...
39914
39915     /**
39916      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
39917      * @param {String} id The id of the div to use <b>or create</b>
39918      * @param {String} text The text for the tab
39919      * @param {String} content (optional) Content to put in the TabPanelItem body
39920      * @param {Boolean} closable (optional) True to create a close icon on the tab
39921      * @return {Roo.TabPanelItem} The created TabPanelItem
39922      */
39923     addTab : function(id, text, content, closable, tpl)
39924     {
39925         var item = new Roo.bootstrap.panel.TabItem({
39926             panel: this,
39927             id : id,
39928             text : text,
39929             closable : closable,
39930             tpl : tpl
39931         });
39932         this.addTabItem(item);
39933         if(content){
39934             item.setContent(content);
39935         }
39936         return item;
39937     },
39938
39939     /**
39940      * Returns the {@link Roo.TabPanelItem} with the specified id/index
39941      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
39942      * @return {Roo.TabPanelItem}
39943      */
39944     getTab : function(id){
39945         return this.items[id];
39946     },
39947
39948     /**
39949      * Hides the {@link Roo.TabPanelItem} with the specified id/index
39950      * @param {String/Number} id The id or index of the TabPanelItem to hide.
39951      */
39952     hideTab : function(id){
39953         var t = this.items[id];
39954         if(!t.isHidden()){
39955            t.setHidden(true);
39956            this.hiddenCount++;
39957            this.autoSizeTabs();
39958         }
39959     },
39960
39961     /**
39962      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
39963      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
39964      */
39965     unhideTab : function(id){
39966         var t = this.items[id];
39967         if(t.isHidden()){
39968            t.setHidden(false);
39969            this.hiddenCount--;
39970            this.autoSizeTabs();
39971         }
39972     },
39973
39974     /**
39975      * Adds an existing {@link Roo.TabPanelItem}.
39976      * @param {Roo.TabPanelItem} item The TabPanelItem to add
39977      */
39978     addTabItem : function(item)
39979     {
39980         this.items[item.id] = item;
39981         this.items.push(item);
39982         this.autoSizeTabs();
39983       //  if(this.resizeTabs){
39984     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
39985   //         this.autoSizeTabs();
39986 //        }else{
39987 //            item.autoSize();
39988        // }
39989     },
39990
39991     /**
39992      * Removes a {@link Roo.TabPanelItem}.
39993      * @param {String/Number} id The id or index of the TabPanelItem to remove.
39994      */
39995     removeTab : function(id){
39996         var items = this.items;
39997         var tab = items[id];
39998         if(!tab) { return; }
39999         var index = items.indexOf(tab);
40000         if(this.active == tab && items.length > 1){
40001             var newTab = this.getNextAvailable(index);
40002             if(newTab) {
40003                 newTab.activate();
40004             }
40005         }
40006         this.stripEl.dom.removeChild(tab.pnode.dom);
40007         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40008             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40009         }
40010         items.splice(index, 1);
40011         delete this.items[tab.id];
40012         tab.fireEvent("close", tab);
40013         tab.purgeListeners();
40014         this.autoSizeTabs();
40015     },
40016
40017     getNextAvailable : function(start){
40018         var items = this.items;
40019         var index = start;
40020         // look for a next tab that will slide over to
40021         // replace the one being removed
40022         while(index < items.length){
40023             var item = items[++index];
40024             if(item && !item.isHidden()){
40025                 return item;
40026             }
40027         }
40028         // if one isn't found select the previous tab (on the left)
40029         index = start;
40030         while(index >= 0){
40031             var item = items[--index];
40032             if(item && !item.isHidden()){
40033                 return item;
40034             }
40035         }
40036         return null;
40037     },
40038
40039     /**
40040      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40041      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40042      */
40043     disableTab : function(id){
40044         var tab = this.items[id];
40045         if(tab && this.active != tab){
40046             tab.disable();
40047         }
40048     },
40049
40050     /**
40051      * Enables a {@link Roo.TabPanelItem} that is disabled.
40052      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40053      */
40054     enableTab : function(id){
40055         var tab = this.items[id];
40056         tab.enable();
40057     },
40058
40059     /**
40060      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40061      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40062      * @return {Roo.TabPanelItem} The TabPanelItem.
40063      */
40064     activate : function(id)
40065     {
40066         //Roo.log('activite:'  + id);
40067         
40068         var tab = this.items[id];
40069         if(!tab){
40070             return null;
40071         }
40072         if(tab == this.active || tab.disabled){
40073             return tab;
40074         }
40075         var e = {};
40076         this.fireEvent("beforetabchange", this, e, tab);
40077         if(e.cancel !== true && !tab.disabled){
40078             if(this.active){
40079                 this.active.hide();
40080             }
40081             this.active = this.items[id];
40082             this.active.show();
40083             this.fireEvent("tabchange", this, this.active);
40084         }
40085         return tab;
40086     },
40087
40088     /**
40089      * Gets the active {@link Roo.TabPanelItem}.
40090      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40091      */
40092     getActiveTab : function(){
40093         return this.active;
40094     },
40095
40096     /**
40097      * Updates the tab body element to fit the height of the container element
40098      * for overflow scrolling
40099      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40100      */
40101     syncHeight : function(targetHeight){
40102         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40103         var bm = this.bodyEl.getMargins();
40104         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40105         this.bodyEl.setHeight(newHeight);
40106         return newHeight;
40107     },
40108
40109     onResize : function(){
40110         if(this.monitorResize){
40111             this.autoSizeTabs();
40112         }
40113     },
40114
40115     /**
40116      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40117      */
40118     beginUpdate : function(){
40119         this.updating = true;
40120     },
40121
40122     /**
40123      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40124      */
40125     endUpdate : function(){
40126         this.updating = false;
40127         this.autoSizeTabs();
40128     },
40129
40130     /**
40131      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40132      */
40133     autoSizeTabs : function()
40134     {
40135         var count = this.items.length;
40136         var vcount = count - this.hiddenCount;
40137         
40138         if (vcount < 2) {
40139             this.stripEl.hide();
40140         } else {
40141             this.stripEl.show();
40142         }
40143         
40144         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40145             return;
40146         }
40147         
40148         
40149         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40150         var availWidth = Math.floor(w / vcount);
40151         var b = this.stripBody;
40152         if(b.getWidth() > w){
40153             var tabs = this.items;
40154             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40155             if(availWidth < this.minTabWidth){
40156                 /*if(!this.sleft){    // incomplete scrolling code
40157                     this.createScrollButtons();
40158                 }
40159                 this.showScroll();
40160                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40161             }
40162         }else{
40163             if(this.currentTabWidth < this.preferredTabWidth){
40164                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40165             }
40166         }
40167     },
40168
40169     /**
40170      * Returns the number of tabs in this TabPanel.
40171      * @return {Number}
40172      */
40173      getCount : function(){
40174          return this.items.length;
40175      },
40176
40177     /**
40178      * Resizes all the tabs to the passed width
40179      * @param {Number} The new width
40180      */
40181     setTabWidth : function(width){
40182         this.currentTabWidth = width;
40183         for(var i = 0, len = this.items.length; i < len; i++) {
40184                 if(!this.items[i].isHidden()) {
40185                 this.items[i].setWidth(width);
40186             }
40187         }
40188     },
40189
40190     /**
40191      * Destroys this TabPanel
40192      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40193      */
40194     destroy : function(removeEl){
40195         Roo.EventManager.removeResizeListener(this.onResize, this);
40196         for(var i = 0, len = this.items.length; i < len; i++){
40197             this.items[i].purgeListeners();
40198         }
40199         if(removeEl === true){
40200             this.el.update("");
40201             this.el.remove();
40202         }
40203     },
40204     
40205     createStrip : function(container)
40206     {
40207         var strip = document.createElement("nav");
40208         strip.className = Roo.bootstrap.version == 4 ?
40209             "navbar-light bg-light" : 
40210             "navbar navbar-default"; //"x-tabs-wrap";
40211         container.appendChild(strip);
40212         return strip;
40213     },
40214     
40215     createStripList : function(strip)
40216     {
40217         // div wrapper for retard IE
40218         // returns the "tr" element.
40219         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40220         //'<div class="x-tabs-strip-wrap">'+
40221           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40222           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40223         return strip.firstChild; //.firstChild.firstChild.firstChild;
40224     },
40225     createBody : function(container)
40226     {
40227         var body = document.createElement("div");
40228         Roo.id(body, "tab-body");
40229         //Roo.fly(body).addClass("x-tabs-body");
40230         Roo.fly(body).addClass("tab-content");
40231         container.appendChild(body);
40232         return body;
40233     },
40234     createItemBody :function(bodyEl, id){
40235         var body = Roo.getDom(id);
40236         if(!body){
40237             body = document.createElement("div");
40238             body.id = id;
40239         }
40240         //Roo.fly(body).addClass("x-tabs-item-body");
40241         Roo.fly(body).addClass("tab-pane");
40242          bodyEl.insertBefore(body, bodyEl.firstChild);
40243         return body;
40244     },
40245     /** @private */
40246     createStripElements :  function(stripEl, text, closable, tpl)
40247     {
40248         var td = document.createElement("li"); // was td..
40249         td.className = 'nav-item';
40250         
40251         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40252         
40253         
40254         stripEl.appendChild(td);
40255         /*if(closable){
40256             td.className = "x-tabs-closable";
40257             if(!this.closeTpl){
40258                 this.closeTpl = new Roo.Template(
40259                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40260                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40261                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40262                 );
40263             }
40264             var el = this.closeTpl.overwrite(td, {"text": text});
40265             var close = el.getElementsByTagName("div")[0];
40266             var inner = el.getElementsByTagName("em")[0];
40267             return {"el": el, "close": close, "inner": inner};
40268         } else {
40269         */
40270         // not sure what this is..
40271 //            if(!this.tabTpl){
40272                 //this.tabTpl = new Roo.Template(
40273                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40274                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40275                 //);
40276 //                this.tabTpl = new Roo.Template(
40277 //                   '<a href="#">' +
40278 //                   '<span unselectable="on"' +
40279 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40280 //                            ' >{text}</span></a>'
40281 //                );
40282 //                
40283 //            }
40284
40285
40286             var template = tpl || this.tabTpl || false;
40287             
40288             if(!template){
40289                 template =  new Roo.Template(
40290                         Roo.bootstrap.version == 4 ? 
40291                             (
40292                                 '<a class="nav-link" href="#" unselectable="on"' +
40293                                      (this.disableTooltips ? '' : ' title="{text}"') +
40294                                      ' >{text}</a>'
40295                             ) : (
40296                                 '<a class="nav-link" href="#">' +
40297                                 '<span unselectable="on"' +
40298                                          (this.disableTooltips ? '' : ' title="{text}"') +
40299                                     ' >{text}</span></a>'
40300                             )
40301                 );
40302             }
40303             
40304             switch (typeof(template)) {
40305                 case 'object' :
40306                     break;
40307                 case 'string' :
40308                     template = new Roo.Template(template);
40309                     break;
40310                 default :
40311                     break;
40312             }
40313             
40314             var el = template.overwrite(td, {"text": text});
40315             
40316             var inner = el.getElementsByTagName("span")[0];
40317             
40318             return {"el": el, "inner": inner};
40319             
40320     }
40321         
40322     
40323 });
40324
40325 /**
40326  * @class Roo.TabPanelItem
40327  * @extends Roo.util.Observable
40328  * Represents an individual item (tab plus body) in a TabPanel.
40329  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40330  * @param {String} id The id of this TabPanelItem
40331  * @param {String} text The text for the tab of this TabPanelItem
40332  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40333  */
40334 Roo.bootstrap.panel.TabItem = function(config){
40335     /**
40336      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40337      * @type Roo.TabPanel
40338      */
40339     this.tabPanel = config.panel;
40340     /**
40341      * The id for this TabPanelItem
40342      * @type String
40343      */
40344     this.id = config.id;
40345     /** @private */
40346     this.disabled = false;
40347     /** @private */
40348     this.text = config.text;
40349     /** @private */
40350     this.loaded = false;
40351     this.closable = config.closable;
40352
40353     /**
40354      * The body element for this TabPanelItem.
40355      * @type Roo.Element
40356      */
40357     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40358     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40359     this.bodyEl.setStyle("display", "block");
40360     this.bodyEl.setStyle("zoom", "1");
40361     //this.hideAction();
40362
40363     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40364     /** @private */
40365     this.el = Roo.get(els.el);
40366     this.inner = Roo.get(els.inner, true);
40367      this.textEl = Roo.bootstrap.version == 4 ?
40368         this.el : Roo.get(this.el.dom.firstChild, true);
40369
40370     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40371     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40372
40373     
40374 //    this.el.on("mousedown", this.onTabMouseDown, this);
40375     this.el.on("click", this.onTabClick, this);
40376     /** @private */
40377     if(config.closable){
40378         var c = Roo.get(els.close, true);
40379         c.dom.title = this.closeText;
40380         c.addClassOnOver("close-over");
40381         c.on("click", this.closeClick, this);
40382      }
40383
40384     this.addEvents({
40385          /**
40386          * @event activate
40387          * Fires when this tab becomes the active tab.
40388          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40389          * @param {Roo.TabPanelItem} this
40390          */
40391         "activate": true,
40392         /**
40393          * @event beforeclose
40394          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40395          * @param {Roo.TabPanelItem} this
40396          * @param {Object} e Set cancel to true on this object to cancel the close.
40397          */
40398         "beforeclose": true,
40399         /**
40400          * @event close
40401          * Fires when this tab is closed.
40402          * @param {Roo.TabPanelItem} this
40403          */
40404          "close": true,
40405         /**
40406          * @event deactivate
40407          * Fires when this tab is no longer the active tab.
40408          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40409          * @param {Roo.TabPanelItem} this
40410          */
40411          "deactivate" : true
40412     });
40413     this.hidden = false;
40414
40415     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40416 };
40417
40418 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40419            {
40420     purgeListeners : function(){
40421        Roo.util.Observable.prototype.purgeListeners.call(this);
40422        this.el.removeAllListeners();
40423     },
40424     /**
40425      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40426      */
40427     show : function(){
40428         this.status_node.addClass("active");
40429         this.showAction();
40430         if(Roo.isOpera){
40431             this.tabPanel.stripWrap.repaint();
40432         }
40433         this.fireEvent("activate", this.tabPanel, this);
40434     },
40435
40436     /**
40437      * Returns true if this tab is the active tab.
40438      * @return {Boolean}
40439      */
40440     isActive : function(){
40441         return this.tabPanel.getActiveTab() == this;
40442     },
40443
40444     /**
40445      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40446      */
40447     hide : function(){
40448         this.status_node.removeClass("active");
40449         this.hideAction();
40450         this.fireEvent("deactivate", this.tabPanel, this);
40451     },
40452
40453     hideAction : function(){
40454         this.bodyEl.hide();
40455         this.bodyEl.setStyle("position", "absolute");
40456         this.bodyEl.setLeft("-20000px");
40457         this.bodyEl.setTop("-20000px");
40458     },
40459
40460     showAction : function(){
40461         this.bodyEl.setStyle("position", "relative");
40462         this.bodyEl.setTop("");
40463         this.bodyEl.setLeft("");
40464         this.bodyEl.show();
40465     },
40466
40467     /**
40468      * Set the tooltip for the tab.
40469      * @param {String} tooltip The tab's tooltip
40470      */
40471     setTooltip : function(text){
40472         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40473             this.textEl.dom.qtip = text;
40474             this.textEl.dom.removeAttribute('title');
40475         }else{
40476             this.textEl.dom.title = text;
40477         }
40478     },
40479
40480     onTabClick : function(e){
40481         e.preventDefault();
40482         this.tabPanel.activate(this.id);
40483     },
40484
40485     onTabMouseDown : function(e){
40486         e.preventDefault();
40487         this.tabPanel.activate(this.id);
40488     },
40489 /*
40490     getWidth : function(){
40491         return this.inner.getWidth();
40492     },
40493
40494     setWidth : function(width){
40495         var iwidth = width - this.linode.getPadding("lr");
40496         this.inner.setWidth(iwidth);
40497         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40498         this.linode.setWidth(width);
40499     },
40500 */
40501     /**
40502      * Show or hide the tab
40503      * @param {Boolean} hidden True to hide or false to show.
40504      */
40505     setHidden : function(hidden){
40506         this.hidden = hidden;
40507         this.linode.setStyle("display", hidden ? "none" : "");
40508     },
40509
40510     /**
40511      * Returns true if this tab is "hidden"
40512      * @return {Boolean}
40513      */
40514     isHidden : function(){
40515         return this.hidden;
40516     },
40517
40518     /**
40519      * Returns the text for this tab
40520      * @return {String}
40521      */
40522     getText : function(){
40523         return this.text;
40524     },
40525     /*
40526     autoSize : function(){
40527         //this.el.beginMeasure();
40528         this.textEl.setWidth(1);
40529         /*
40530          *  #2804 [new] Tabs in Roojs
40531          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40532          */
40533         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40534         //this.el.endMeasure();
40535     //},
40536
40537     /**
40538      * Sets the text for the tab (Note: this also sets the tooltip text)
40539      * @param {String} text The tab's text and tooltip
40540      */
40541     setText : function(text){
40542         this.text = text;
40543         this.textEl.update(text);
40544         this.setTooltip(text);
40545         //if(!this.tabPanel.resizeTabs){
40546         //    this.autoSize();
40547         //}
40548     },
40549     /**
40550      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40551      */
40552     activate : function(){
40553         this.tabPanel.activate(this.id);
40554     },
40555
40556     /**
40557      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40558      */
40559     disable : function(){
40560         if(this.tabPanel.active != this){
40561             this.disabled = true;
40562             this.status_node.addClass("disabled");
40563         }
40564     },
40565
40566     /**
40567      * Enables this TabPanelItem if it was previously disabled.
40568      */
40569     enable : function(){
40570         this.disabled = false;
40571         this.status_node.removeClass("disabled");
40572     },
40573
40574     /**
40575      * Sets the content for this TabPanelItem.
40576      * @param {String} content The content
40577      * @param {Boolean} loadScripts true to look for and load scripts
40578      */
40579     setContent : function(content, loadScripts){
40580         this.bodyEl.update(content, loadScripts);
40581     },
40582
40583     /**
40584      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40585      * @return {Roo.UpdateManager} The UpdateManager
40586      */
40587     getUpdateManager : function(){
40588         return this.bodyEl.getUpdateManager();
40589     },
40590
40591     /**
40592      * Set a URL to be used to load the content for this TabPanelItem.
40593      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40594      * @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)
40595      * @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)
40596      * @return {Roo.UpdateManager} The UpdateManager
40597      */
40598     setUrl : function(url, params, loadOnce){
40599         if(this.refreshDelegate){
40600             this.un('activate', this.refreshDelegate);
40601         }
40602         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40603         this.on("activate", this.refreshDelegate);
40604         return this.bodyEl.getUpdateManager();
40605     },
40606
40607     /** @private */
40608     _handleRefresh : function(url, params, loadOnce){
40609         if(!loadOnce || !this.loaded){
40610             var updater = this.bodyEl.getUpdateManager();
40611             updater.update(url, params, this._setLoaded.createDelegate(this));
40612         }
40613     },
40614
40615     /**
40616      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
40617      *   Will fail silently if the setUrl method has not been called.
40618      *   This does not activate the panel, just updates its content.
40619      */
40620     refresh : function(){
40621         if(this.refreshDelegate){
40622            this.loaded = false;
40623            this.refreshDelegate();
40624         }
40625     },
40626
40627     /** @private */
40628     _setLoaded : function(){
40629         this.loaded = true;
40630     },
40631
40632     /** @private */
40633     closeClick : function(e){
40634         var o = {};
40635         e.stopEvent();
40636         this.fireEvent("beforeclose", this, o);
40637         if(o.cancel !== true){
40638             this.tabPanel.removeTab(this.id);
40639         }
40640     },
40641     /**
40642      * The text displayed in the tooltip for the close icon.
40643      * @type String
40644      */
40645     closeText : "Close this tab"
40646 });
40647 /**
40648 *    This script refer to:
40649 *    Title: International Telephone Input
40650 *    Author: Jack O'Connor
40651 *    Code version:  v12.1.12
40652 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40653 **/
40654
40655 Roo.bootstrap.PhoneInputData = function() {
40656     var d = [
40657       [
40658         "Afghanistan (‫افغانستان‬‎)",
40659         "af",
40660         "93"
40661       ],
40662       [
40663         "Albania (Shqipëri)",
40664         "al",
40665         "355"
40666       ],
40667       [
40668         "Algeria (‫الجزائر‬‎)",
40669         "dz",
40670         "213"
40671       ],
40672       [
40673         "American Samoa",
40674         "as",
40675         "1684"
40676       ],
40677       [
40678         "Andorra",
40679         "ad",
40680         "376"
40681       ],
40682       [
40683         "Angola",
40684         "ao",
40685         "244"
40686       ],
40687       [
40688         "Anguilla",
40689         "ai",
40690         "1264"
40691       ],
40692       [
40693         "Antigua and Barbuda",
40694         "ag",
40695         "1268"
40696       ],
40697       [
40698         "Argentina",
40699         "ar",
40700         "54"
40701       ],
40702       [
40703         "Armenia (Հայաստան)",
40704         "am",
40705         "374"
40706       ],
40707       [
40708         "Aruba",
40709         "aw",
40710         "297"
40711       ],
40712       [
40713         "Australia",
40714         "au",
40715         "61",
40716         0
40717       ],
40718       [
40719         "Austria (Österreich)",
40720         "at",
40721         "43"
40722       ],
40723       [
40724         "Azerbaijan (Azərbaycan)",
40725         "az",
40726         "994"
40727       ],
40728       [
40729         "Bahamas",
40730         "bs",
40731         "1242"
40732       ],
40733       [
40734         "Bahrain (‫البحرين‬‎)",
40735         "bh",
40736         "973"
40737       ],
40738       [
40739         "Bangladesh (বাংলাদেশ)",
40740         "bd",
40741         "880"
40742       ],
40743       [
40744         "Barbados",
40745         "bb",
40746         "1246"
40747       ],
40748       [
40749         "Belarus (Беларусь)",
40750         "by",
40751         "375"
40752       ],
40753       [
40754         "Belgium (België)",
40755         "be",
40756         "32"
40757       ],
40758       [
40759         "Belize",
40760         "bz",
40761         "501"
40762       ],
40763       [
40764         "Benin (Bénin)",
40765         "bj",
40766         "229"
40767       ],
40768       [
40769         "Bermuda",
40770         "bm",
40771         "1441"
40772       ],
40773       [
40774         "Bhutan (འབྲུག)",
40775         "bt",
40776         "975"
40777       ],
40778       [
40779         "Bolivia",
40780         "bo",
40781         "591"
40782       ],
40783       [
40784         "Bosnia and Herzegovina (Босна и Херцеговина)",
40785         "ba",
40786         "387"
40787       ],
40788       [
40789         "Botswana",
40790         "bw",
40791         "267"
40792       ],
40793       [
40794         "Brazil (Brasil)",
40795         "br",
40796         "55"
40797       ],
40798       [
40799         "British Indian Ocean Territory",
40800         "io",
40801         "246"
40802       ],
40803       [
40804         "British Virgin Islands",
40805         "vg",
40806         "1284"
40807       ],
40808       [
40809         "Brunei",
40810         "bn",
40811         "673"
40812       ],
40813       [
40814         "Bulgaria (България)",
40815         "bg",
40816         "359"
40817       ],
40818       [
40819         "Burkina Faso",
40820         "bf",
40821         "226"
40822       ],
40823       [
40824         "Burundi (Uburundi)",
40825         "bi",
40826         "257"
40827       ],
40828       [
40829         "Cambodia (កម្ពុជា)",
40830         "kh",
40831         "855"
40832       ],
40833       [
40834         "Cameroon (Cameroun)",
40835         "cm",
40836         "237"
40837       ],
40838       [
40839         "Canada",
40840         "ca",
40841         "1",
40842         1,
40843         ["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"]
40844       ],
40845       [
40846         "Cape Verde (Kabu Verdi)",
40847         "cv",
40848         "238"
40849       ],
40850       [
40851         "Caribbean Netherlands",
40852         "bq",
40853         "599",
40854         1
40855       ],
40856       [
40857         "Cayman Islands",
40858         "ky",
40859         "1345"
40860       ],
40861       [
40862         "Central African Republic (République centrafricaine)",
40863         "cf",
40864         "236"
40865       ],
40866       [
40867         "Chad (Tchad)",
40868         "td",
40869         "235"
40870       ],
40871       [
40872         "Chile",
40873         "cl",
40874         "56"
40875       ],
40876       [
40877         "China (中国)",
40878         "cn",
40879         "86"
40880       ],
40881       [
40882         "Christmas Island",
40883         "cx",
40884         "61",
40885         2
40886       ],
40887       [
40888         "Cocos (Keeling) Islands",
40889         "cc",
40890         "61",
40891         1
40892       ],
40893       [
40894         "Colombia",
40895         "co",
40896         "57"
40897       ],
40898       [
40899         "Comoros (‫جزر القمر‬‎)",
40900         "km",
40901         "269"
40902       ],
40903       [
40904         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
40905         "cd",
40906         "243"
40907       ],
40908       [
40909         "Congo (Republic) (Congo-Brazzaville)",
40910         "cg",
40911         "242"
40912       ],
40913       [
40914         "Cook Islands",
40915         "ck",
40916         "682"
40917       ],
40918       [
40919         "Costa Rica",
40920         "cr",
40921         "506"
40922       ],
40923       [
40924         "Côte d’Ivoire",
40925         "ci",
40926         "225"
40927       ],
40928       [
40929         "Croatia (Hrvatska)",
40930         "hr",
40931         "385"
40932       ],
40933       [
40934         "Cuba",
40935         "cu",
40936         "53"
40937       ],
40938       [
40939         "Curaçao",
40940         "cw",
40941         "599",
40942         0
40943       ],
40944       [
40945         "Cyprus (Κύπρος)",
40946         "cy",
40947         "357"
40948       ],
40949       [
40950         "Czech Republic (Česká republika)",
40951         "cz",
40952         "420"
40953       ],
40954       [
40955         "Denmark (Danmark)",
40956         "dk",
40957         "45"
40958       ],
40959       [
40960         "Djibouti",
40961         "dj",
40962         "253"
40963       ],
40964       [
40965         "Dominica",
40966         "dm",
40967         "1767"
40968       ],
40969       [
40970         "Dominican Republic (República Dominicana)",
40971         "do",
40972         "1",
40973         2,
40974         ["809", "829", "849"]
40975       ],
40976       [
40977         "Ecuador",
40978         "ec",
40979         "593"
40980       ],
40981       [
40982         "Egypt (‫مصر‬‎)",
40983         "eg",
40984         "20"
40985       ],
40986       [
40987         "El Salvador",
40988         "sv",
40989         "503"
40990       ],
40991       [
40992         "Equatorial Guinea (Guinea Ecuatorial)",
40993         "gq",
40994         "240"
40995       ],
40996       [
40997         "Eritrea",
40998         "er",
40999         "291"
41000       ],
41001       [
41002         "Estonia (Eesti)",
41003         "ee",
41004         "372"
41005       ],
41006       [
41007         "Ethiopia",
41008         "et",
41009         "251"
41010       ],
41011       [
41012         "Falkland Islands (Islas Malvinas)",
41013         "fk",
41014         "500"
41015       ],
41016       [
41017         "Faroe Islands (Føroyar)",
41018         "fo",
41019         "298"
41020       ],
41021       [
41022         "Fiji",
41023         "fj",
41024         "679"
41025       ],
41026       [
41027         "Finland (Suomi)",
41028         "fi",
41029         "358",
41030         0
41031       ],
41032       [
41033         "France",
41034         "fr",
41035         "33"
41036       ],
41037       [
41038         "French Guiana (Guyane française)",
41039         "gf",
41040         "594"
41041       ],
41042       [
41043         "French Polynesia (Polynésie française)",
41044         "pf",
41045         "689"
41046       ],
41047       [
41048         "Gabon",
41049         "ga",
41050         "241"
41051       ],
41052       [
41053         "Gambia",
41054         "gm",
41055         "220"
41056       ],
41057       [
41058         "Georgia (საქართველო)",
41059         "ge",
41060         "995"
41061       ],
41062       [
41063         "Germany (Deutschland)",
41064         "de",
41065         "49"
41066       ],
41067       [
41068         "Ghana (Gaana)",
41069         "gh",
41070         "233"
41071       ],
41072       [
41073         "Gibraltar",
41074         "gi",
41075         "350"
41076       ],
41077       [
41078         "Greece (Ελλάδα)",
41079         "gr",
41080         "30"
41081       ],
41082       [
41083         "Greenland (Kalaallit Nunaat)",
41084         "gl",
41085         "299"
41086       ],
41087       [
41088         "Grenada",
41089         "gd",
41090         "1473"
41091       ],
41092       [
41093         "Guadeloupe",
41094         "gp",
41095         "590",
41096         0
41097       ],
41098       [
41099         "Guam",
41100         "gu",
41101         "1671"
41102       ],
41103       [
41104         "Guatemala",
41105         "gt",
41106         "502"
41107       ],
41108       [
41109         "Guernsey",
41110         "gg",
41111         "44",
41112         1
41113       ],
41114       [
41115         "Guinea (Guinée)",
41116         "gn",
41117         "224"
41118       ],
41119       [
41120         "Guinea-Bissau (Guiné Bissau)",
41121         "gw",
41122         "245"
41123       ],
41124       [
41125         "Guyana",
41126         "gy",
41127         "592"
41128       ],
41129       [
41130         "Haiti",
41131         "ht",
41132         "509"
41133       ],
41134       [
41135         "Honduras",
41136         "hn",
41137         "504"
41138       ],
41139       [
41140         "Hong Kong (香港)",
41141         "hk",
41142         "852"
41143       ],
41144       [
41145         "Hungary (Magyarország)",
41146         "hu",
41147         "36"
41148       ],
41149       [
41150         "Iceland (Ísland)",
41151         "is",
41152         "354"
41153       ],
41154       [
41155         "India (भारत)",
41156         "in",
41157         "91"
41158       ],
41159       [
41160         "Indonesia",
41161         "id",
41162         "62"
41163       ],
41164       [
41165         "Iran (‫ایران‬‎)",
41166         "ir",
41167         "98"
41168       ],
41169       [
41170         "Iraq (‫العراق‬‎)",
41171         "iq",
41172         "964"
41173       ],
41174       [
41175         "Ireland",
41176         "ie",
41177         "353"
41178       ],
41179       [
41180         "Isle of Man",
41181         "im",
41182         "44",
41183         2
41184       ],
41185       [
41186         "Israel (‫ישראל‬‎)",
41187         "il",
41188         "972"
41189       ],
41190       [
41191         "Italy (Italia)",
41192         "it",
41193         "39",
41194         0
41195       ],
41196       [
41197         "Jamaica",
41198         "jm",
41199         "1876"
41200       ],
41201       [
41202         "Japan (日本)",
41203         "jp",
41204         "81"
41205       ],
41206       [
41207         "Jersey",
41208         "je",
41209         "44",
41210         3
41211       ],
41212       [
41213         "Jordan (‫الأردن‬‎)",
41214         "jo",
41215         "962"
41216       ],
41217       [
41218         "Kazakhstan (Казахстан)",
41219         "kz",
41220         "7",
41221         1
41222       ],
41223       [
41224         "Kenya",
41225         "ke",
41226         "254"
41227       ],
41228       [
41229         "Kiribati",
41230         "ki",
41231         "686"
41232       ],
41233       [
41234         "Kosovo",
41235         "xk",
41236         "383"
41237       ],
41238       [
41239         "Kuwait (‫الكويت‬‎)",
41240         "kw",
41241         "965"
41242       ],
41243       [
41244         "Kyrgyzstan (Кыргызстан)",
41245         "kg",
41246         "996"
41247       ],
41248       [
41249         "Laos (ລາວ)",
41250         "la",
41251         "856"
41252       ],
41253       [
41254         "Latvia (Latvija)",
41255         "lv",
41256         "371"
41257       ],
41258       [
41259         "Lebanon (‫لبنان‬‎)",
41260         "lb",
41261         "961"
41262       ],
41263       [
41264         "Lesotho",
41265         "ls",
41266         "266"
41267       ],
41268       [
41269         "Liberia",
41270         "lr",
41271         "231"
41272       ],
41273       [
41274         "Libya (‫ليبيا‬‎)",
41275         "ly",
41276         "218"
41277       ],
41278       [
41279         "Liechtenstein",
41280         "li",
41281         "423"
41282       ],
41283       [
41284         "Lithuania (Lietuva)",
41285         "lt",
41286         "370"
41287       ],
41288       [
41289         "Luxembourg",
41290         "lu",
41291         "352"
41292       ],
41293       [
41294         "Macau (澳門)",
41295         "mo",
41296         "853"
41297       ],
41298       [
41299         "Macedonia (FYROM) (Македонија)",
41300         "mk",
41301         "389"
41302       ],
41303       [
41304         "Madagascar (Madagasikara)",
41305         "mg",
41306         "261"
41307       ],
41308       [
41309         "Malawi",
41310         "mw",
41311         "265"
41312       ],
41313       [
41314         "Malaysia",
41315         "my",
41316         "60"
41317       ],
41318       [
41319         "Maldives",
41320         "mv",
41321         "960"
41322       ],
41323       [
41324         "Mali",
41325         "ml",
41326         "223"
41327       ],
41328       [
41329         "Malta",
41330         "mt",
41331         "356"
41332       ],
41333       [
41334         "Marshall Islands",
41335         "mh",
41336         "692"
41337       ],
41338       [
41339         "Martinique",
41340         "mq",
41341         "596"
41342       ],
41343       [
41344         "Mauritania (‫موريتانيا‬‎)",
41345         "mr",
41346         "222"
41347       ],
41348       [
41349         "Mauritius (Moris)",
41350         "mu",
41351         "230"
41352       ],
41353       [
41354         "Mayotte",
41355         "yt",
41356         "262",
41357         1
41358       ],
41359       [
41360         "Mexico (México)",
41361         "mx",
41362         "52"
41363       ],
41364       [
41365         "Micronesia",
41366         "fm",
41367         "691"
41368       ],
41369       [
41370         "Moldova (Republica Moldova)",
41371         "md",
41372         "373"
41373       ],
41374       [
41375         "Monaco",
41376         "mc",
41377         "377"
41378       ],
41379       [
41380         "Mongolia (Монгол)",
41381         "mn",
41382         "976"
41383       ],
41384       [
41385         "Montenegro (Crna Gora)",
41386         "me",
41387         "382"
41388       ],
41389       [
41390         "Montserrat",
41391         "ms",
41392         "1664"
41393       ],
41394       [
41395         "Morocco (‫المغرب‬‎)",
41396         "ma",
41397         "212",
41398         0
41399       ],
41400       [
41401         "Mozambique (Moçambique)",
41402         "mz",
41403         "258"
41404       ],
41405       [
41406         "Myanmar (Burma) (မြန်မာ)",
41407         "mm",
41408         "95"
41409       ],
41410       [
41411         "Namibia (Namibië)",
41412         "na",
41413         "264"
41414       ],
41415       [
41416         "Nauru",
41417         "nr",
41418         "674"
41419       ],
41420       [
41421         "Nepal (नेपाल)",
41422         "np",
41423         "977"
41424       ],
41425       [
41426         "Netherlands (Nederland)",
41427         "nl",
41428         "31"
41429       ],
41430       [
41431         "New Caledonia (Nouvelle-Calédonie)",
41432         "nc",
41433         "687"
41434       ],
41435       [
41436         "New Zealand",
41437         "nz",
41438         "64"
41439       ],
41440       [
41441         "Nicaragua",
41442         "ni",
41443         "505"
41444       ],
41445       [
41446         "Niger (Nijar)",
41447         "ne",
41448         "227"
41449       ],
41450       [
41451         "Nigeria",
41452         "ng",
41453         "234"
41454       ],
41455       [
41456         "Niue",
41457         "nu",
41458         "683"
41459       ],
41460       [
41461         "Norfolk Island",
41462         "nf",
41463         "672"
41464       ],
41465       [
41466         "North Korea (조선 민주주의 인민 공화국)",
41467         "kp",
41468         "850"
41469       ],
41470       [
41471         "Northern Mariana Islands",
41472         "mp",
41473         "1670"
41474       ],
41475       [
41476         "Norway (Norge)",
41477         "no",
41478         "47",
41479         0
41480       ],
41481       [
41482         "Oman (‫عُمان‬‎)",
41483         "om",
41484         "968"
41485       ],
41486       [
41487         "Pakistan (‫پاکستان‬‎)",
41488         "pk",
41489         "92"
41490       ],
41491       [
41492         "Palau",
41493         "pw",
41494         "680"
41495       ],
41496       [
41497         "Palestine (‫فلسطين‬‎)",
41498         "ps",
41499         "970"
41500       ],
41501       [
41502         "Panama (Panamá)",
41503         "pa",
41504         "507"
41505       ],
41506       [
41507         "Papua New Guinea",
41508         "pg",
41509         "675"
41510       ],
41511       [
41512         "Paraguay",
41513         "py",
41514         "595"
41515       ],
41516       [
41517         "Peru (Perú)",
41518         "pe",
41519         "51"
41520       ],
41521       [
41522         "Philippines",
41523         "ph",
41524         "63"
41525       ],
41526       [
41527         "Poland (Polska)",
41528         "pl",
41529         "48"
41530       ],
41531       [
41532         "Portugal",
41533         "pt",
41534         "351"
41535       ],
41536       [
41537         "Puerto Rico",
41538         "pr",
41539         "1",
41540         3,
41541         ["787", "939"]
41542       ],
41543       [
41544         "Qatar (‫قطر‬‎)",
41545         "qa",
41546         "974"
41547       ],
41548       [
41549         "Réunion (La Réunion)",
41550         "re",
41551         "262",
41552         0
41553       ],
41554       [
41555         "Romania (România)",
41556         "ro",
41557         "40"
41558       ],
41559       [
41560         "Russia (Россия)",
41561         "ru",
41562         "7",
41563         0
41564       ],
41565       [
41566         "Rwanda",
41567         "rw",
41568         "250"
41569       ],
41570       [
41571         "Saint Barthélemy",
41572         "bl",
41573         "590",
41574         1
41575       ],
41576       [
41577         "Saint Helena",
41578         "sh",
41579         "290"
41580       ],
41581       [
41582         "Saint Kitts and Nevis",
41583         "kn",
41584         "1869"
41585       ],
41586       [
41587         "Saint Lucia",
41588         "lc",
41589         "1758"
41590       ],
41591       [
41592         "Saint Martin (Saint-Martin (partie française))",
41593         "mf",
41594         "590",
41595         2
41596       ],
41597       [
41598         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41599         "pm",
41600         "508"
41601       ],
41602       [
41603         "Saint Vincent and the Grenadines",
41604         "vc",
41605         "1784"
41606       ],
41607       [
41608         "Samoa",
41609         "ws",
41610         "685"
41611       ],
41612       [
41613         "San Marino",
41614         "sm",
41615         "378"
41616       ],
41617       [
41618         "São Tomé and Príncipe (São Tomé e Príncipe)",
41619         "st",
41620         "239"
41621       ],
41622       [
41623         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
41624         "sa",
41625         "966"
41626       ],
41627       [
41628         "Senegal (Sénégal)",
41629         "sn",
41630         "221"
41631       ],
41632       [
41633         "Serbia (Србија)",
41634         "rs",
41635         "381"
41636       ],
41637       [
41638         "Seychelles",
41639         "sc",
41640         "248"
41641       ],
41642       [
41643         "Sierra Leone",
41644         "sl",
41645         "232"
41646       ],
41647       [
41648         "Singapore",
41649         "sg",
41650         "65"
41651       ],
41652       [
41653         "Sint Maarten",
41654         "sx",
41655         "1721"
41656       ],
41657       [
41658         "Slovakia (Slovensko)",
41659         "sk",
41660         "421"
41661       ],
41662       [
41663         "Slovenia (Slovenija)",
41664         "si",
41665         "386"
41666       ],
41667       [
41668         "Solomon Islands",
41669         "sb",
41670         "677"
41671       ],
41672       [
41673         "Somalia (Soomaaliya)",
41674         "so",
41675         "252"
41676       ],
41677       [
41678         "South Africa",
41679         "za",
41680         "27"
41681       ],
41682       [
41683         "South Korea (대한민국)",
41684         "kr",
41685         "82"
41686       ],
41687       [
41688         "South Sudan (‫جنوب السودان‬‎)",
41689         "ss",
41690         "211"
41691       ],
41692       [
41693         "Spain (España)",
41694         "es",
41695         "34"
41696       ],
41697       [
41698         "Sri Lanka (ශ්‍රී ලංකාව)",
41699         "lk",
41700         "94"
41701       ],
41702       [
41703         "Sudan (‫السودان‬‎)",
41704         "sd",
41705         "249"
41706       ],
41707       [
41708         "Suriname",
41709         "sr",
41710         "597"
41711       ],
41712       [
41713         "Svalbard and Jan Mayen",
41714         "sj",
41715         "47",
41716         1
41717       ],
41718       [
41719         "Swaziland",
41720         "sz",
41721         "268"
41722       ],
41723       [
41724         "Sweden (Sverige)",
41725         "se",
41726         "46"
41727       ],
41728       [
41729         "Switzerland (Schweiz)",
41730         "ch",
41731         "41"
41732       ],
41733       [
41734         "Syria (‫سوريا‬‎)",
41735         "sy",
41736         "963"
41737       ],
41738       [
41739         "Taiwan (台灣)",
41740         "tw",
41741         "886"
41742       ],
41743       [
41744         "Tajikistan",
41745         "tj",
41746         "992"
41747       ],
41748       [
41749         "Tanzania",
41750         "tz",
41751         "255"
41752       ],
41753       [
41754         "Thailand (ไทย)",
41755         "th",
41756         "66"
41757       ],
41758       [
41759         "Timor-Leste",
41760         "tl",
41761         "670"
41762       ],
41763       [
41764         "Togo",
41765         "tg",
41766         "228"
41767       ],
41768       [
41769         "Tokelau",
41770         "tk",
41771         "690"
41772       ],
41773       [
41774         "Tonga",
41775         "to",
41776         "676"
41777       ],
41778       [
41779         "Trinidad and Tobago",
41780         "tt",
41781         "1868"
41782       ],
41783       [
41784         "Tunisia (‫تونس‬‎)",
41785         "tn",
41786         "216"
41787       ],
41788       [
41789         "Turkey (Türkiye)",
41790         "tr",
41791         "90"
41792       ],
41793       [
41794         "Turkmenistan",
41795         "tm",
41796         "993"
41797       ],
41798       [
41799         "Turks and Caicos Islands",
41800         "tc",
41801         "1649"
41802       ],
41803       [
41804         "Tuvalu",
41805         "tv",
41806         "688"
41807       ],
41808       [
41809         "U.S. Virgin Islands",
41810         "vi",
41811         "1340"
41812       ],
41813       [
41814         "Uganda",
41815         "ug",
41816         "256"
41817       ],
41818       [
41819         "Ukraine (Україна)",
41820         "ua",
41821         "380"
41822       ],
41823       [
41824         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
41825         "ae",
41826         "971"
41827       ],
41828       [
41829         "United Kingdom",
41830         "gb",
41831         "44",
41832         0
41833       ],
41834       [
41835         "United States",
41836         "us",
41837         "1",
41838         0
41839       ],
41840       [
41841         "Uruguay",
41842         "uy",
41843         "598"
41844       ],
41845       [
41846         "Uzbekistan (Oʻzbekiston)",
41847         "uz",
41848         "998"
41849       ],
41850       [
41851         "Vanuatu",
41852         "vu",
41853         "678"
41854       ],
41855       [
41856         "Vatican City (Città del Vaticano)",
41857         "va",
41858         "39",
41859         1
41860       ],
41861       [
41862         "Venezuela",
41863         "ve",
41864         "58"
41865       ],
41866       [
41867         "Vietnam (Việt Nam)",
41868         "vn",
41869         "84"
41870       ],
41871       [
41872         "Wallis and Futuna (Wallis-et-Futuna)",
41873         "wf",
41874         "681"
41875       ],
41876       [
41877         "Western Sahara (‫الصحراء الغربية‬‎)",
41878         "eh",
41879         "212",
41880         1
41881       ],
41882       [
41883         "Yemen (‫اليمن‬‎)",
41884         "ye",
41885         "967"
41886       ],
41887       [
41888         "Zambia",
41889         "zm",
41890         "260"
41891       ],
41892       [
41893         "Zimbabwe",
41894         "zw",
41895         "263"
41896       ],
41897       [
41898         "Åland Islands",
41899         "ax",
41900         "358",
41901         1
41902       ]
41903   ];
41904   
41905   return d;
41906 }/**
41907 *    This script refer to:
41908 *    Title: International Telephone Input
41909 *    Author: Jack O'Connor
41910 *    Code version:  v12.1.12
41911 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41912 **/
41913
41914 /**
41915  * @class Roo.bootstrap.PhoneInput
41916  * @extends Roo.bootstrap.TriggerField
41917  * An input with International dial-code selection
41918  
41919  * @cfg {String} defaultDialCode default '+852'
41920  * @cfg {Array} preferedCountries default []
41921   
41922  * @constructor
41923  * Create a new PhoneInput.
41924  * @param {Object} config Configuration options
41925  */
41926
41927 Roo.bootstrap.PhoneInput = function(config) {
41928     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
41929 };
41930
41931 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
41932         
41933         listWidth: undefined,
41934         
41935         selectedClass: 'active',
41936         
41937         invalidClass : "has-warning",
41938         
41939         validClass: 'has-success',
41940         
41941         allowed: '0123456789',
41942         
41943         max_length: 15,
41944         
41945         /**
41946          * @cfg {String} defaultDialCode The default dial code when initializing the input
41947          */
41948         defaultDialCode: '+852',
41949         
41950         /**
41951          * @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
41952          */
41953         preferedCountries: false,
41954         
41955         getAutoCreate : function()
41956         {
41957             var data = Roo.bootstrap.PhoneInputData();
41958             var align = this.labelAlign || this.parentLabelAlign();
41959             var id = Roo.id();
41960             
41961             this.allCountries = [];
41962             this.dialCodeMapping = [];
41963             
41964             for (var i = 0; i < data.length; i++) {
41965               var c = data[i];
41966               this.allCountries[i] = {
41967                 name: c[0],
41968                 iso2: c[1],
41969                 dialCode: c[2],
41970                 priority: c[3] || 0,
41971                 areaCodes: c[4] || null
41972               };
41973               this.dialCodeMapping[c[2]] = {
41974                   name: c[0],
41975                   iso2: c[1],
41976                   priority: c[3] || 0,
41977                   areaCodes: c[4] || null
41978               };
41979             }
41980             
41981             var cfg = {
41982                 cls: 'form-group',
41983                 cn: []
41984             };
41985             
41986             var input =  {
41987                 tag: 'input',
41988                 id : id,
41989                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
41990                 maxlength: this.max_length,
41991                 cls : 'form-control tel-input',
41992                 autocomplete: 'new-password'
41993             };
41994             
41995             var hiddenInput = {
41996                 tag: 'input',
41997                 type: 'hidden',
41998                 cls: 'hidden-tel-input'
41999             };
42000             
42001             if (this.name) {
42002                 hiddenInput.name = this.name;
42003             }
42004             
42005             if (this.disabled) {
42006                 input.disabled = true;
42007             }
42008             
42009             var flag_container = {
42010                 tag: 'div',
42011                 cls: 'flag-box',
42012                 cn: [
42013                     {
42014                         tag: 'div',
42015                         cls: 'flag'
42016                     },
42017                     {
42018                         tag: 'div',
42019                         cls: 'caret'
42020                     }
42021                 ]
42022             };
42023             
42024             var box = {
42025                 tag: 'div',
42026                 cls: this.hasFeedback ? 'has-feedback' : '',
42027                 cn: [
42028                     hiddenInput,
42029                     input,
42030                     {
42031                         tag: 'input',
42032                         cls: 'dial-code-holder',
42033                         disabled: true
42034                     }
42035                 ]
42036             };
42037             
42038             var container = {
42039                 cls: 'roo-select2-container input-group',
42040                 cn: [
42041                     flag_container,
42042                     box
42043                 ]
42044             };
42045             
42046             if (this.fieldLabel.length) {
42047                 var indicator = {
42048                     tag: 'i',
42049                     tooltip: 'This field is required'
42050                 };
42051                 
42052                 var label = {
42053                     tag: 'label',
42054                     'for':  id,
42055                     cls: 'control-label',
42056                     cn: []
42057                 };
42058                 
42059                 var label_text = {
42060                     tag: 'span',
42061                     html: this.fieldLabel
42062                 };
42063                 
42064                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42065                 label.cn = [
42066                     indicator,
42067                     label_text
42068                 ];
42069                 
42070                 if(this.indicatorpos == 'right') {
42071                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42072                     label.cn = [
42073                         label_text,
42074                         indicator
42075                     ];
42076                 }
42077                 
42078                 if(align == 'left') {
42079                     container = {
42080                         tag: 'div',
42081                         cn: [
42082                             container
42083                         ]
42084                     };
42085                     
42086                     if(this.labelWidth > 12){
42087                         label.style = "width: " + this.labelWidth + 'px';
42088                     }
42089                     if(this.labelWidth < 13 && this.labelmd == 0){
42090                         this.labelmd = this.labelWidth;
42091                     }
42092                     if(this.labellg > 0){
42093                         label.cls += ' col-lg-' + this.labellg;
42094                         input.cls += ' col-lg-' + (12 - this.labellg);
42095                     }
42096                     if(this.labelmd > 0){
42097                         label.cls += ' col-md-' + this.labelmd;
42098                         container.cls += ' col-md-' + (12 - this.labelmd);
42099                     }
42100                     if(this.labelsm > 0){
42101                         label.cls += ' col-sm-' + this.labelsm;
42102                         container.cls += ' col-sm-' + (12 - this.labelsm);
42103                     }
42104                     if(this.labelxs > 0){
42105                         label.cls += ' col-xs-' + this.labelxs;
42106                         container.cls += ' col-xs-' + (12 - this.labelxs);
42107                     }
42108                 }
42109             }
42110             
42111             cfg.cn = [
42112                 label,
42113                 container
42114             ];
42115             
42116             var settings = this;
42117             
42118             ['xs','sm','md','lg'].map(function(size){
42119                 if (settings[size]) {
42120                     cfg.cls += ' col-' + size + '-' + settings[size];
42121                 }
42122             });
42123             
42124             this.store = new Roo.data.Store({
42125                 proxy : new Roo.data.MemoryProxy({}),
42126                 reader : new Roo.data.JsonReader({
42127                     fields : [
42128                         {
42129                             'name' : 'name',
42130                             'type' : 'string'
42131                         },
42132                         {
42133                             'name' : 'iso2',
42134                             'type' : 'string'
42135                         },
42136                         {
42137                             'name' : 'dialCode',
42138                             'type' : 'string'
42139                         },
42140                         {
42141                             'name' : 'priority',
42142                             'type' : 'string'
42143                         },
42144                         {
42145                             'name' : 'areaCodes',
42146                             'type' : 'string'
42147                         }
42148                     ]
42149                 })
42150             });
42151             
42152             if(!this.preferedCountries) {
42153                 this.preferedCountries = [
42154                     'hk',
42155                     'gb',
42156                     'us'
42157                 ];
42158             }
42159             
42160             var p = this.preferedCountries.reverse();
42161             
42162             if(p) {
42163                 for (var i = 0; i < p.length; i++) {
42164                     for (var j = 0; j < this.allCountries.length; j++) {
42165                         if(this.allCountries[j].iso2 == p[i]) {
42166                             var t = this.allCountries[j];
42167                             this.allCountries.splice(j,1);
42168                             this.allCountries.unshift(t);
42169                         }
42170                     } 
42171                 }
42172             }
42173             
42174             this.store.proxy.data = {
42175                 success: true,
42176                 data: this.allCountries
42177             };
42178             
42179             return cfg;
42180         },
42181         
42182         initEvents : function()
42183         {
42184             this.createList();
42185             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42186             
42187             this.indicator = this.indicatorEl();
42188             this.flag = this.flagEl();
42189             this.dialCodeHolder = this.dialCodeHolderEl();
42190             
42191             this.trigger = this.el.select('div.flag-box',true).first();
42192             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42193             
42194             var _this = this;
42195             
42196             (function(){
42197                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42198                 _this.list.setWidth(lw);
42199             }).defer(100);
42200             
42201             this.list.on('mouseover', this.onViewOver, this);
42202             this.list.on('mousemove', this.onViewMove, this);
42203             this.inputEl().on("keyup", this.onKeyUp, this);
42204             this.inputEl().on("keypress", this.onKeyPress, this);
42205             
42206             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42207
42208             this.view = new Roo.View(this.list, this.tpl, {
42209                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42210             });
42211             
42212             this.view.on('click', this.onViewClick, this);
42213             this.setValue(this.defaultDialCode);
42214         },
42215         
42216         onTriggerClick : function(e)
42217         {
42218             Roo.log('trigger click');
42219             if(this.disabled){
42220                 return;
42221             }
42222             
42223             if(this.isExpanded()){
42224                 this.collapse();
42225                 this.hasFocus = false;
42226             }else {
42227                 this.store.load({});
42228                 this.hasFocus = true;
42229                 this.expand();
42230             }
42231         },
42232         
42233         isExpanded : function()
42234         {
42235             return this.list.isVisible();
42236         },
42237         
42238         collapse : function()
42239         {
42240             if(!this.isExpanded()){
42241                 return;
42242             }
42243             this.list.hide();
42244             Roo.get(document).un('mousedown', this.collapseIf, this);
42245             Roo.get(document).un('mousewheel', this.collapseIf, this);
42246             this.fireEvent('collapse', this);
42247             this.validate();
42248         },
42249         
42250         expand : function()
42251         {
42252             Roo.log('expand');
42253
42254             if(this.isExpanded() || !this.hasFocus){
42255                 return;
42256             }
42257             
42258             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42259             this.list.setWidth(lw);
42260             
42261             this.list.show();
42262             this.restrictHeight();
42263             
42264             Roo.get(document).on('mousedown', this.collapseIf, this);
42265             Roo.get(document).on('mousewheel', this.collapseIf, this);
42266             
42267             this.fireEvent('expand', this);
42268         },
42269         
42270         restrictHeight : function()
42271         {
42272             this.list.alignTo(this.inputEl(), this.listAlign);
42273             this.list.alignTo(this.inputEl(), this.listAlign);
42274         },
42275         
42276         onViewOver : function(e, t)
42277         {
42278             if(this.inKeyMode){
42279                 return;
42280             }
42281             var item = this.view.findItemFromChild(t);
42282             
42283             if(item){
42284                 var index = this.view.indexOf(item);
42285                 this.select(index, false);
42286             }
42287         },
42288
42289         // private
42290         onViewClick : function(view, doFocus, el, e)
42291         {
42292             var index = this.view.getSelectedIndexes()[0];
42293             
42294             var r = this.store.getAt(index);
42295             
42296             if(r){
42297                 this.onSelect(r, index);
42298             }
42299             if(doFocus !== false && !this.blockFocus){
42300                 this.inputEl().focus();
42301             }
42302         },
42303         
42304         onViewMove : function(e, t)
42305         {
42306             this.inKeyMode = false;
42307         },
42308         
42309         select : function(index, scrollIntoView)
42310         {
42311             this.selectedIndex = index;
42312             this.view.select(index);
42313             if(scrollIntoView !== false){
42314                 var el = this.view.getNode(index);
42315                 if(el){
42316                     this.list.scrollChildIntoView(el, false);
42317                 }
42318             }
42319         },
42320         
42321         createList : function()
42322         {
42323             this.list = Roo.get(document.body).createChild({
42324                 tag: 'ul',
42325                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42326                 style: 'display:none'
42327             });
42328             
42329             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42330         },
42331         
42332         collapseIf : function(e)
42333         {
42334             var in_combo  = e.within(this.el);
42335             var in_list =  e.within(this.list);
42336             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42337             
42338             if (in_combo || in_list || is_list) {
42339                 return;
42340             }
42341             this.collapse();
42342         },
42343         
42344         onSelect : function(record, index)
42345         {
42346             if(this.fireEvent('beforeselect', this, record, index) !== false){
42347                 
42348                 this.setFlagClass(record.data.iso2);
42349                 this.setDialCode(record.data.dialCode);
42350                 this.hasFocus = false;
42351                 this.collapse();
42352                 this.fireEvent('select', this, record, index);
42353             }
42354         },
42355         
42356         flagEl : function()
42357         {
42358             var flag = this.el.select('div.flag',true).first();
42359             if(!flag){
42360                 return false;
42361             }
42362             return flag;
42363         },
42364         
42365         dialCodeHolderEl : function()
42366         {
42367             var d = this.el.select('input.dial-code-holder',true).first();
42368             if(!d){
42369                 return false;
42370             }
42371             return d;
42372         },
42373         
42374         setDialCode : function(v)
42375         {
42376             this.dialCodeHolder.dom.value = '+'+v;
42377         },
42378         
42379         setFlagClass : function(n)
42380         {
42381             this.flag.dom.className = 'flag '+n;
42382         },
42383         
42384         getValue : function()
42385         {
42386             var v = this.inputEl().getValue();
42387             if(this.dialCodeHolder) {
42388                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42389             }
42390             return v;
42391         },
42392         
42393         setValue : function(v)
42394         {
42395             var d = this.getDialCode(v);
42396             
42397             //invalid dial code
42398             if(v.length == 0 || !d || d.length == 0) {
42399                 if(this.rendered){
42400                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42401                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42402                 }
42403                 return;
42404             }
42405             
42406             //valid dial code
42407             this.setFlagClass(this.dialCodeMapping[d].iso2);
42408             this.setDialCode(d);
42409             this.inputEl().dom.value = v.replace('+'+d,'');
42410             this.hiddenEl().dom.value = this.getValue();
42411             
42412             this.validate();
42413         },
42414         
42415         getDialCode : function(v)
42416         {
42417             v = v ||  '';
42418             
42419             if (v.length == 0) {
42420                 return this.dialCodeHolder.dom.value;
42421             }
42422             
42423             var dialCode = "";
42424             if (v.charAt(0) != "+") {
42425                 return false;
42426             }
42427             var numericChars = "";
42428             for (var i = 1; i < v.length; i++) {
42429               var c = v.charAt(i);
42430               if (!isNaN(c)) {
42431                 numericChars += c;
42432                 if (this.dialCodeMapping[numericChars]) {
42433                   dialCode = v.substr(1, i);
42434                 }
42435                 if (numericChars.length == 4) {
42436                   break;
42437                 }
42438               }
42439             }
42440             return dialCode;
42441         },
42442         
42443         reset : function()
42444         {
42445             this.setValue(this.defaultDialCode);
42446             this.validate();
42447         },
42448         
42449         hiddenEl : function()
42450         {
42451             return this.el.select('input.hidden-tel-input',true).first();
42452         },
42453         
42454         // after setting val
42455         onKeyUp : function(e){
42456             this.setValue(this.getValue());
42457         },
42458         
42459         onKeyPress : function(e){
42460             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42461                 e.stopEvent();
42462             }
42463         }
42464         
42465 });
42466 /**
42467  * @class Roo.bootstrap.MoneyField
42468  * @extends Roo.bootstrap.ComboBox
42469  * Bootstrap MoneyField class
42470  * 
42471  * @constructor
42472  * Create a new MoneyField.
42473  * @param {Object} config Configuration options
42474  */
42475
42476 Roo.bootstrap.MoneyField = function(config) {
42477     
42478     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42479     
42480 };
42481
42482 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42483     
42484     /**
42485      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42486      */
42487     allowDecimals : true,
42488     /**
42489      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42490      */
42491     decimalSeparator : ".",
42492     /**
42493      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42494      */
42495     decimalPrecision : 0,
42496     /**
42497      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42498      */
42499     allowNegative : true,
42500     /**
42501      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42502      */
42503     allowZero: true,
42504     /**
42505      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42506      */
42507     minValue : Number.NEGATIVE_INFINITY,
42508     /**
42509      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42510      */
42511     maxValue : Number.MAX_VALUE,
42512     /**
42513      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42514      */
42515     minText : "The minimum value for this field is {0}",
42516     /**
42517      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42518      */
42519     maxText : "The maximum value for this field is {0}",
42520     /**
42521      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
42522      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42523      */
42524     nanText : "{0} is not a valid number",
42525     /**
42526      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42527      */
42528     castInt : true,
42529     /**
42530      * @cfg {String} defaults currency of the MoneyField
42531      * value should be in lkey
42532      */
42533     defaultCurrency : false,
42534     /**
42535      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42536      */
42537     thousandsDelimiter : false,
42538     /**
42539      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42540      */
42541     max_length: false,
42542     
42543     inputlg : 9,
42544     inputmd : 9,
42545     inputsm : 9,
42546     inputxs : 6,
42547     
42548     store : false,
42549     
42550     getAutoCreate : function()
42551     {
42552         var align = this.labelAlign || this.parentLabelAlign();
42553         
42554         var id = Roo.id();
42555
42556         var cfg = {
42557             cls: 'form-group',
42558             cn: []
42559         };
42560
42561         var input =  {
42562             tag: 'input',
42563             id : id,
42564             cls : 'form-control roo-money-amount-input',
42565             autocomplete: 'new-password'
42566         };
42567         
42568         var hiddenInput = {
42569             tag: 'input',
42570             type: 'hidden',
42571             id: Roo.id(),
42572             cls: 'hidden-number-input'
42573         };
42574         
42575         if(this.max_length) {
42576             input.maxlength = this.max_length; 
42577         }
42578         
42579         if (this.name) {
42580             hiddenInput.name = this.name;
42581         }
42582
42583         if (this.disabled) {
42584             input.disabled = true;
42585         }
42586
42587         var clg = 12 - this.inputlg;
42588         var cmd = 12 - this.inputmd;
42589         var csm = 12 - this.inputsm;
42590         var cxs = 12 - this.inputxs;
42591         
42592         var container = {
42593             tag : 'div',
42594             cls : 'row roo-money-field',
42595             cn : [
42596                 {
42597                     tag : 'div',
42598                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42599                     cn : [
42600                         {
42601                             tag : 'div',
42602                             cls: 'roo-select2-container input-group',
42603                             cn: [
42604                                 {
42605                                     tag : 'input',
42606                                     cls : 'form-control roo-money-currency-input',
42607                                     autocomplete: 'new-password',
42608                                     readOnly : 1,
42609                                     name : this.currencyName
42610                                 },
42611                                 {
42612                                     tag :'span',
42613                                     cls : 'input-group-addon',
42614                                     cn : [
42615                                         {
42616                                             tag: 'span',
42617                                             cls: 'caret'
42618                                         }
42619                                     ]
42620                                 }
42621                             ]
42622                         }
42623                     ]
42624                 },
42625                 {
42626                     tag : 'div',
42627                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42628                     cn : [
42629                         {
42630                             tag: 'div',
42631                             cls: this.hasFeedback ? 'has-feedback' : '',
42632                             cn: [
42633                                 input
42634                             ]
42635                         }
42636                     ]
42637                 }
42638             ]
42639             
42640         };
42641         
42642         if (this.fieldLabel.length) {
42643             var indicator = {
42644                 tag: 'i',
42645                 tooltip: 'This field is required'
42646             };
42647
42648             var label = {
42649                 tag: 'label',
42650                 'for':  id,
42651                 cls: 'control-label',
42652                 cn: []
42653             };
42654
42655             var label_text = {
42656                 tag: 'span',
42657                 html: this.fieldLabel
42658             };
42659
42660             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42661             label.cn = [
42662                 indicator,
42663                 label_text
42664             ];
42665
42666             if(this.indicatorpos == 'right') {
42667                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42668                 label.cn = [
42669                     label_text,
42670                     indicator
42671                 ];
42672             }
42673
42674             if(align == 'left') {
42675                 container = {
42676                     tag: 'div',
42677                     cn: [
42678                         container
42679                     ]
42680                 };
42681
42682                 if(this.labelWidth > 12){
42683                     label.style = "width: " + this.labelWidth + 'px';
42684                 }
42685                 if(this.labelWidth < 13 && this.labelmd == 0){
42686                     this.labelmd = this.labelWidth;
42687                 }
42688                 if(this.labellg > 0){
42689                     label.cls += ' col-lg-' + this.labellg;
42690                     input.cls += ' col-lg-' + (12 - this.labellg);
42691                 }
42692                 if(this.labelmd > 0){
42693                     label.cls += ' col-md-' + this.labelmd;
42694                     container.cls += ' col-md-' + (12 - this.labelmd);
42695                 }
42696                 if(this.labelsm > 0){
42697                     label.cls += ' col-sm-' + this.labelsm;
42698                     container.cls += ' col-sm-' + (12 - this.labelsm);
42699                 }
42700                 if(this.labelxs > 0){
42701                     label.cls += ' col-xs-' + this.labelxs;
42702                     container.cls += ' col-xs-' + (12 - this.labelxs);
42703                 }
42704             }
42705         }
42706
42707         cfg.cn = [
42708             label,
42709             container,
42710             hiddenInput
42711         ];
42712         
42713         var settings = this;
42714
42715         ['xs','sm','md','lg'].map(function(size){
42716             if (settings[size]) {
42717                 cfg.cls += ' col-' + size + '-' + settings[size];
42718             }
42719         });
42720         
42721         return cfg;
42722     },
42723     
42724     initEvents : function()
42725     {
42726         this.indicator = this.indicatorEl();
42727         
42728         this.initCurrencyEvent();
42729         
42730         this.initNumberEvent();
42731     },
42732     
42733     initCurrencyEvent : function()
42734     {
42735         if (!this.store) {
42736             throw "can not find store for combo";
42737         }
42738         
42739         this.store = Roo.factory(this.store, Roo.data);
42740         this.store.parent = this;
42741         
42742         this.createList();
42743         
42744         this.triggerEl = this.el.select('.input-group-addon', true).first();
42745         
42746         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42747         
42748         var _this = this;
42749         
42750         (function(){
42751             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42752             _this.list.setWidth(lw);
42753         }).defer(100);
42754         
42755         this.list.on('mouseover', this.onViewOver, this);
42756         this.list.on('mousemove', this.onViewMove, this);
42757         this.list.on('scroll', this.onViewScroll, this);
42758         
42759         if(!this.tpl){
42760             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42761         }
42762         
42763         this.view = new Roo.View(this.list, this.tpl, {
42764             singleSelect:true, store: this.store, selectedClass: this.selectedClass
42765         });
42766         
42767         this.view.on('click', this.onViewClick, this);
42768         
42769         this.store.on('beforeload', this.onBeforeLoad, this);
42770         this.store.on('load', this.onLoad, this);
42771         this.store.on('loadexception', this.onLoadException, this);
42772         
42773         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
42774             "up" : function(e){
42775                 this.inKeyMode = true;
42776                 this.selectPrev();
42777             },
42778
42779             "down" : function(e){
42780                 if(!this.isExpanded()){
42781                     this.onTriggerClick();
42782                 }else{
42783                     this.inKeyMode = true;
42784                     this.selectNext();
42785                 }
42786             },
42787
42788             "enter" : function(e){
42789                 this.collapse();
42790                 
42791                 if(this.fireEvent("specialkey", this, e)){
42792                     this.onViewClick(false);
42793                 }
42794                 
42795                 return true;
42796             },
42797
42798             "esc" : function(e){
42799                 this.collapse();
42800             },
42801
42802             "tab" : function(e){
42803                 this.collapse();
42804                 
42805                 if(this.fireEvent("specialkey", this, e)){
42806                     this.onViewClick(false);
42807                 }
42808                 
42809                 return true;
42810             },
42811
42812             scope : this,
42813
42814             doRelay : function(foo, bar, hname){
42815                 if(hname == 'down' || this.scope.isExpanded()){
42816                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42817                 }
42818                 return true;
42819             },
42820
42821             forceKeyDown: true
42822         });
42823         
42824         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
42825         
42826     },
42827     
42828     initNumberEvent : function(e)
42829     {
42830         this.inputEl().on("keydown" , this.fireKey,  this);
42831         this.inputEl().on("focus", this.onFocus,  this);
42832         this.inputEl().on("blur", this.onBlur,  this);
42833         
42834         this.inputEl().relayEvent('keyup', this);
42835         
42836         if(this.indicator){
42837             this.indicator.addClass('invisible');
42838         }
42839  
42840         this.originalValue = this.getValue();
42841         
42842         if(this.validationEvent == 'keyup'){
42843             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
42844             this.inputEl().on('keyup', this.filterValidation, this);
42845         }
42846         else if(this.validationEvent !== false){
42847             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
42848         }
42849         
42850         if(this.selectOnFocus){
42851             this.on("focus", this.preFocus, this);
42852             
42853         }
42854         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
42855             this.inputEl().on("keypress", this.filterKeys, this);
42856         } else {
42857             this.inputEl().relayEvent('keypress', this);
42858         }
42859         
42860         var allowed = "0123456789";
42861         
42862         if(this.allowDecimals){
42863             allowed += this.decimalSeparator;
42864         }
42865         
42866         if(this.allowNegative){
42867             allowed += "-";
42868         }
42869         
42870         if(this.thousandsDelimiter) {
42871             allowed += ",";
42872         }
42873         
42874         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
42875         
42876         var keyPress = function(e){
42877             
42878             var k = e.getKey();
42879             
42880             var c = e.getCharCode();
42881             
42882             if(
42883                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
42884                     allowed.indexOf(String.fromCharCode(c)) === -1
42885             ){
42886                 e.stopEvent();
42887                 return;
42888             }
42889             
42890             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
42891                 return;
42892             }
42893             
42894             if(allowed.indexOf(String.fromCharCode(c)) === -1){
42895                 e.stopEvent();
42896             }
42897         };
42898         
42899         this.inputEl().on("keypress", keyPress, this);
42900         
42901     },
42902     
42903     onTriggerClick : function(e)
42904     {   
42905         if(this.disabled){
42906             return;
42907         }
42908         
42909         this.page = 0;
42910         this.loadNext = false;
42911         
42912         if(this.isExpanded()){
42913             this.collapse();
42914             return;
42915         }
42916         
42917         this.hasFocus = true;
42918         
42919         if(this.triggerAction == 'all') {
42920             this.doQuery(this.allQuery, true);
42921             return;
42922         }
42923         
42924         this.doQuery(this.getRawValue());
42925     },
42926     
42927     getCurrency : function()
42928     {   
42929         var v = this.currencyEl().getValue();
42930         
42931         return v;
42932     },
42933     
42934     restrictHeight : function()
42935     {
42936         this.list.alignTo(this.currencyEl(), this.listAlign);
42937         this.list.alignTo(this.currencyEl(), this.listAlign);
42938     },
42939     
42940     onViewClick : function(view, doFocus, el, e)
42941     {
42942         var index = this.view.getSelectedIndexes()[0];
42943         
42944         var r = this.store.getAt(index);
42945         
42946         if(r){
42947             this.onSelect(r, index);
42948         }
42949     },
42950     
42951     onSelect : function(record, index){
42952         
42953         if(this.fireEvent('beforeselect', this, record, index) !== false){
42954         
42955             this.setFromCurrencyData(index > -1 ? record.data : false);
42956             
42957             this.collapse();
42958             
42959             this.fireEvent('select', this, record, index);
42960         }
42961     },
42962     
42963     setFromCurrencyData : function(o)
42964     {
42965         var currency = '';
42966         
42967         this.lastCurrency = o;
42968         
42969         if (this.currencyField) {
42970             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
42971         } else {
42972             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
42973         }
42974         
42975         this.lastSelectionText = currency;
42976         
42977         //setting default currency
42978         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
42979             this.setCurrency(this.defaultCurrency);
42980             return;
42981         }
42982         
42983         this.setCurrency(currency);
42984     },
42985     
42986     setFromData : function(o)
42987     {
42988         var c = {};
42989         
42990         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
42991         
42992         this.setFromCurrencyData(c);
42993         
42994         var value = '';
42995         
42996         if (this.name) {
42997             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
42998         } else {
42999             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43000         }
43001         
43002         this.setValue(value);
43003         
43004     },
43005     
43006     setCurrency : function(v)
43007     {   
43008         this.currencyValue = v;
43009         
43010         if(this.rendered){
43011             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43012             this.validate();
43013         }
43014     },
43015     
43016     setValue : function(v)
43017     {
43018         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43019         
43020         this.value = v;
43021         
43022         if(this.rendered){
43023             
43024             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43025             
43026             this.inputEl().dom.value = (v == '') ? '' :
43027                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43028             
43029             if(!this.allowZero && v === '0') {
43030                 this.hiddenEl().dom.value = '';
43031                 this.inputEl().dom.value = '';
43032             }
43033             
43034             this.validate();
43035         }
43036     },
43037     
43038     getRawValue : function()
43039     {
43040         var v = this.inputEl().getValue();
43041         
43042         return v;
43043     },
43044     
43045     getValue : function()
43046     {
43047         return this.fixPrecision(this.parseValue(this.getRawValue()));
43048     },
43049     
43050     parseValue : function(value)
43051     {
43052         if(this.thousandsDelimiter) {
43053             value += "";
43054             r = new RegExp(",", "g");
43055             value = value.replace(r, "");
43056         }
43057         
43058         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43059         return isNaN(value) ? '' : value;
43060         
43061     },
43062     
43063     fixPrecision : function(value)
43064     {
43065         if(this.thousandsDelimiter) {
43066             value += "";
43067             r = new RegExp(",", "g");
43068             value = value.replace(r, "");
43069         }
43070         
43071         var nan = isNaN(value);
43072         
43073         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43074             return nan ? '' : value;
43075         }
43076         return parseFloat(value).toFixed(this.decimalPrecision);
43077     },
43078     
43079     decimalPrecisionFcn : function(v)
43080     {
43081         return Math.floor(v);
43082     },
43083     
43084     validateValue : function(value)
43085     {
43086         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43087             return false;
43088         }
43089         
43090         var num = this.parseValue(value);
43091         
43092         if(isNaN(num)){
43093             this.markInvalid(String.format(this.nanText, value));
43094             return false;
43095         }
43096         
43097         if(num < this.minValue){
43098             this.markInvalid(String.format(this.minText, this.minValue));
43099             return false;
43100         }
43101         
43102         if(num > this.maxValue){
43103             this.markInvalid(String.format(this.maxText, this.maxValue));
43104             return false;
43105         }
43106         
43107         return true;
43108     },
43109     
43110     validate : function()
43111     {
43112         if(this.disabled || this.allowBlank){
43113             this.markValid();
43114             return true;
43115         }
43116         
43117         var currency = this.getCurrency();
43118         
43119         if(this.validateValue(this.getRawValue()) && currency.length){
43120             this.markValid();
43121             return true;
43122         }
43123         
43124         this.markInvalid();
43125         return false;
43126     },
43127     
43128     getName: function()
43129     {
43130         return this.name;
43131     },
43132     
43133     beforeBlur : function()
43134     {
43135         if(!this.castInt){
43136             return;
43137         }
43138         
43139         var v = this.parseValue(this.getRawValue());
43140         
43141         if(v || v == 0){
43142             this.setValue(v);
43143         }
43144     },
43145     
43146     onBlur : function()
43147     {
43148         this.beforeBlur();
43149         
43150         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43151             //this.el.removeClass(this.focusClass);
43152         }
43153         
43154         this.hasFocus = false;
43155         
43156         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43157             this.validate();
43158         }
43159         
43160         var v = this.getValue();
43161         
43162         if(String(v) !== String(this.startValue)){
43163             this.fireEvent('change', this, v, this.startValue);
43164         }
43165         
43166         this.fireEvent("blur", this);
43167     },
43168     
43169     inputEl : function()
43170     {
43171         return this.el.select('.roo-money-amount-input', true).first();
43172     },
43173     
43174     currencyEl : function()
43175     {
43176         return this.el.select('.roo-money-currency-input', true).first();
43177     },
43178     
43179     hiddenEl : function()
43180     {
43181         return this.el.select('input.hidden-number-input',true).first();
43182     }
43183     
43184 });/**
43185  * @class Roo.bootstrap.BezierSignature
43186  * @extends Roo.bootstrap.Component
43187  * Bootstrap BezierSignature class
43188  * This script refer to:
43189  *    Title: Signature Pad
43190  *    Author: szimek
43191  *    Availability: https://github.com/szimek/signature_pad
43192  *
43193  * @constructor
43194  * Create a new BezierSignature
43195  * @param {Object} config The config object
43196  */
43197
43198 Roo.bootstrap.BezierSignature = function(config){
43199     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43200     this.addEvents({
43201         "resize" : true
43202     });
43203 };
43204
43205 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43206 {
43207      
43208     curve_data: [],
43209     
43210     is_empty: true,
43211     
43212     mouse_btn_down: true,
43213     
43214     /**
43215      * @cfg {int} canvas height
43216      */
43217     canvas_height: '200px',
43218     
43219     /**
43220      * @cfg {float|function} Radius of a single dot.
43221      */ 
43222     dot_size: false,
43223     
43224     /**
43225      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43226      */
43227     min_width: 0.5,
43228     
43229     /**
43230      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43231      */
43232     max_width: 2.5,
43233     
43234     /**
43235      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43236      */
43237     throttle: 16,
43238     
43239     /**
43240      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43241      */
43242     min_distance: 5,
43243     
43244     /**
43245      * @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.
43246      */
43247     bg_color: 'rgba(0, 0, 0, 0)',
43248     
43249     /**
43250      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43251      */
43252     dot_color: 'black',
43253     
43254     /**
43255      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43256      */ 
43257     velocity_filter_weight: 0.7,
43258     
43259     /**
43260      * @cfg {function} Callback when stroke begin. 
43261      */
43262     onBegin: false,
43263     
43264     /**
43265      * @cfg {function} Callback when stroke end.
43266      */
43267     onEnd: false,
43268     
43269     getAutoCreate : function()
43270     {
43271         var cls = 'roo-signature column';
43272         
43273         if(this.cls){
43274             cls += ' ' + this.cls;
43275         }
43276         
43277         var col_sizes = [
43278             'lg',
43279             'md',
43280             'sm',
43281             'xs'
43282         ];
43283         
43284         for(var i = 0; i < col_sizes.length; i++) {
43285             if(this[col_sizes[i]]) {
43286                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43287             }
43288         }
43289         
43290         var cfg = {
43291             tag: 'div',
43292             cls: cls,
43293             cn: [
43294                 {
43295                     tag: 'div',
43296                     cls: 'roo-signature-body',
43297                     cn: [
43298                         {
43299                             tag: 'canvas',
43300                             cls: 'roo-signature-body-canvas',
43301                             height: this.canvas_height,
43302                             width: this.canvas_width
43303                         }
43304                     ]
43305                 },
43306                 {
43307                     tag: 'input',
43308                     type: 'file',
43309                     style: 'display: none'
43310                 }
43311             ]
43312         };
43313         
43314         return cfg;
43315     },
43316     
43317     initEvents: function() 
43318     {
43319         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43320         
43321         var canvas = this.canvasEl();
43322         
43323         // mouse && touch event swapping...
43324         canvas.dom.style.touchAction = 'none';
43325         canvas.dom.style.msTouchAction = 'none';
43326         
43327         this.mouse_btn_down = false;
43328         canvas.on('mousedown', this._handleMouseDown, this);
43329         canvas.on('mousemove', this._handleMouseMove, this);
43330         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43331         
43332         if (window.PointerEvent) {
43333             canvas.on('pointerdown', this._handleMouseDown, this);
43334             canvas.on('pointermove', this._handleMouseMove, this);
43335             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43336         }
43337         
43338         if ('ontouchstart' in window) {
43339             canvas.on('touchstart', this._handleTouchStart, this);
43340             canvas.on('touchmove', this._handleTouchMove, this);
43341             canvas.on('touchend', this._handleTouchEnd, this);
43342         }
43343         
43344         Roo.EventManager.onWindowResize(this.resize, this, true);
43345         
43346         // file input event
43347         this.fileEl().on('change', this.uploadImage, this);
43348         
43349         this.clear();
43350         
43351         this.resize();
43352     },
43353     
43354     resize: function(){
43355         
43356         var canvas = this.canvasEl().dom;
43357         var ctx = this.canvasElCtx();
43358         var img_data = false;
43359         
43360         if(canvas.width > 0) {
43361             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43362         }
43363         // setting canvas width will clean img data
43364         canvas.width = 0;
43365         
43366         var style = window.getComputedStyle ? 
43367             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43368             
43369         var padding_left = parseInt(style.paddingLeft) || 0;
43370         var padding_right = parseInt(style.paddingRight) || 0;
43371         
43372         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43373         
43374         if(img_data) {
43375             ctx.putImageData(img_data, 0, 0);
43376         }
43377     },
43378     
43379     _handleMouseDown: function(e)
43380     {
43381         if (e.browserEvent.which === 1) {
43382             this.mouse_btn_down = true;
43383             this.strokeBegin(e);
43384         }
43385     },
43386     
43387     _handleMouseMove: function (e)
43388     {
43389         if (this.mouse_btn_down) {
43390             this.strokeMoveUpdate(e);
43391         }
43392     },
43393     
43394     _handleMouseUp: function (e)
43395     {
43396         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43397             this.mouse_btn_down = false;
43398             this.strokeEnd(e);
43399         }
43400     },
43401     
43402     _handleTouchStart: function (e) {
43403         
43404         e.preventDefault();
43405         if (e.browserEvent.targetTouches.length === 1) {
43406             // var touch = e.browserEvent.changedTouches[0];
43407             // this.strokeBegin(touch);
43408             
43409              this.strokeBegin(e); // assume e catching the correct xy...
43410         }
43411     },
43412     
43413     _handleTouchMove: function (e) {
43414         e.preventDefault();
43415         // var touch = event.targetTouches[0];
43416         // _this._strokeMoveUpdate(touch);
43417         this.strokeMoveUpdate(e);
43418     },
43419     
43420     _handleTouchEnd: function (e) {
43421         var wasCanvasTouched = e.target === this.canvasEl().dom;
43422         if (wasCanvasTouched) {
43423             e.preventDefault();
43424             // var touch = event.changedTouches[0];
43425             // _this._strokeEnd(touch);
43426             this.strokeEnd(e);
43427         }
43428     },
43429     
43430     reset: function () {
43431         this._lastPoints = [];
43432         this._lastVelocity = 0;
43433         this._lastWidth = (this.min_width + this.max_width) / 2;
43434         this.canvasElCtx().fillStyle = this.dot_color;
43435     },
43436     
43437     strokeMoveUpdate: function(e)
43438     {
43439         this.strokeUpdate(e);
43440         
43441         if (this.throttle) {
43442             this.throttleStroke(this.strokeUpdate, this.throttle);
43443         }
43444         else {
43445             this.strokeUpdate(e);
43446         }
43447     },
43448     
43449     strokeBegin: function(e)
43450     {
43451         var newPointGroup = {
43452             color: this.dot_color,
43453             points: []
43454         };
43455         
43456         if (typeof this.onBegin === 'function') {
43457             this.onBegin(e);
43458         }
43459         
43460         this.curve_data.push(newPointGroup);
43461         this.reset();
43462         this.strokeUpdate(e);
43463     },
43464     
43465     strokeUpdate: function(e)
43466     {
43467         var rect = this.canvasEl().dom.getBoundingClientRect();
43468         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43469         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43470         var lastPoints = lastPointGroup.points;
43471         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43472         var isLastPointTooClose = lastPoint
43473             ? point.distanceTo(lastPoint) <= this.min_distance
43474             : false;
43475         var color = lastPointGroup.color;
43476         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43477             var curve = this.addPoint(point);
43478             if (!lastPoint) {
43479                 this.drawDot({color: color, point: point});
43480             }
43481             else if (curve) {
43482                 this.drawCurve({color: color, curve: curve});
43483             }
43484             lastPoints.push({
43485                 time: point.time,
43486                 x: point.x,
43487                 y: point.y
43488             });
43489         }
43490     },
43491     
43492     strokeEnd: function(e)
43493     {
43494         this.strokeUpdate(e);
43495         if (typeof this.onEnd === 'function') {
43496             this.onEnd(e);
43497         }
43498     },
43499     
43500     addPoint:  function (point) {
43501         var _lastPoints = this._lastPoints;
43502         _lastPoints.push(point);
43503         if (_lastPoints.length > 2) {
43504             if (_lastPoints.length === 3) {
43505                 _lastPoints.unshift(_lastPoints[0]);
43506             }
43507             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43508             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43509             _lastPoints.shift();
43510             return curve;
43511         }
43512         return null;
43513     },
43514     
43515     calculateCurveWidths: function (startPoint, endPoint) {
43516         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43517             (1 - this.velocity_filter_weight) * this._lastVelocity;
43518
43519         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43520         var widths = {
43521             end: newWidth,
43522             start: this._lastWidth
43523         };
43524         
43525         this._lastVelocity = velocity;
43526         this._lastWidth = newWidth;
43527         return widths;
43528     },
43529     
43530     drawDot: function (_a) {
43531         var color = _a.color, point = _a.point;
43532         var ctx = this.canvasElCtx();
43533         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43534         ctx.beginPath();
43535         this.drawCurveSegment(point.x, point.y, width);
43536         ctx.closePath();
43537         ctx.fillStyle = color;
43538         ctx.fill();
43539     },
43540     
43541     drawCurve: function (_a) {
43542         var color = _a.color, curve = _a.curve;
43543         var ctx = this.canvasElCtx();
43544         var widthDelta = curve.endWidth - curve.startWidth;
43545         var drawSteps = Math.floor(curve.length()) * 2;
43546         ctx.beginPath();
43547         ctx.fillStyle = color;
43548         for (var i = 0; i < drawSteps; i += 1) {
43549         var t = i / drawSteps;
43550         var tt = t * t;
43551         var ttt = tt * t;
43552         var u = 1 - t;
43553         var uu = u * u;
43554         var uuu = uu * u;
43555         var x = uuu * curve.startPoint.x;
43556         x += 3 * uu * t * curve.control1.x;
43557         x += 3 * u * tt * curve.control2.x;
43558         x += ttt * curve.endPoint.x;
43559         var y = uuu * curve.startPoint.y;
43560         y += 3 * uu * t * curve.control1.y;
43561         y += 3 * u * tt * curve.control2.y;
43562         y += ttt * curve.endPoint.y;
43563         var width = curve.startWidth + ttt * widthDelta;
43564         this.drawCurveSegment(x, y, width);
43565         }
43566         ctx.closePath();
43567         ctx.fill();
43568     },
43569     
43570     drawCurveSegment: function (x, y, width) {
43571         var ctx = this.canvasElCtx();
43572         ctx.moveTo(x, y);
43573         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43574         this.is_empty = false;
43575     },
43576     
43577     clear: function()
43578     {
43579         var ctx = this.canvasElCtx();
43580         var canvas = this.canvasEl().dom;
43581         ctx.fillStyle = this.bg_color;
43582         ctx.clearRect(0, 0, canvas.width, canvas.height);
43583         ctx.fillRect(0, 0, canvas.width, canvas.height);
43584         this.curve_data = [];
43585         this.reset();
43586         this.is_empty = true;
43587     },
43588     
43589     fileEl: function()
43590     {
43591         return  this.el.select('input',true).first();
43592     },
43593     
43594     canvasEl: function()
43595     {
43596         return this.el.select('canvas',true).first();
43597     },
43598     
43599     canvasElCtx: function()
43600     {
43601         return this.el.select('canvas',true).first().dom.getContext('2d');
43602     },
43603     
43604     getImage: function(type)
43605     {
43606         if(this.is_empty) {
43607             return false;
43608         }
43609         
43610         // encryption ?
43611         return this.canvasEl().dom.toDataURL('image/'+type, 1);
43612     },
43613     
43614     drawFromImage: function(img_src)
43615     {
43616         var img = new Image();
43617         
43618         img.onload = function(){
43619             this.canvasElCtx().drawImage(img, 0, 0);
43620         }.bind(this);
43621         
43622         img.src = img_src;
43623         
43624         this.is_empty = false;
43625     },
43626     
43627     selectImage: function()
43628     {
43629         this.fileEl().dom.click();
43630     },
43631     
43632     uploadImage: function(e)
43633     {
43634         var reader = new FileReader();
43635         
43636         reader.onload = function(e){
43637             var img = new Image();
43638             img.onload = function(){
43639                 this.reset();
43640                 this.canvasElCtx().drawImage(img, 0, 0);
43641             }.bind(this);
43642             img.src = e.target.result;
43643         }.bind(this);
43644         
43645         reader.readAsDataURL(e.target.files[0]);
43646     },
43647     
43648     // Bezier Point Constructor
43649     Point: (function () {
43650         function Point(x, y, time) {
43651             this.x = x;
43652             this.y = y;
43653             this.time = time || Date.now();
43654         }
43655         Point.prototype.distanceTo = function (start) {
43656             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43657         };
43658         Point.prototype.equals = function (other) {
43659             return this.x === other.x && this.y === other.y && this.time === other.time;
43660         };
43661         Point.prototype.velocityFrom = function (start) {
43662             return this.time !== start.time
43663             ? this.distanceTo(start) / (this.time - start.time)
43664             : 0;
43665         };
43666         return Point;
43667     }()),
43668     
43669     
43670     // Bezier Constructor
43671     Bezier: (function () {
43672         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43673             this.startPoint = startPoint;
43674             this.control2 = control2;
43675             this.control1 = control1;
43676             this.endPoint = endPoint;
43677             this.startWidth = startWidth;
43678             this.endWidth = endWidth;
43679         }
43680         Bezier.fromPoints = function (points, widths, scope) {
43681             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43682             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43683             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43684         };
43685         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43686             var dx1 = s1.x - s2.x;
43687             var dy1 = s1.y - s2.y;
43688             var dx2 = s2.x - s3.x;
43689             var dy2 = s2.y - s3.y;
43690             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43691             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43692             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43693             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43694             var dxm = m1.x - m2.x;
43695             var dym = m1.y - m2.y;
43696             var k = l2 / (l1 + l2);
43697             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43698             var tx = s2.x - cm.x;
43699             var ty = s2.y - cm.y;
43700             return {
43701                 c1: new scope.Point(m1.x + tx, m1.y + ty),
43702                 c2: new scope.Point(m2.x + tx, m2.y + ty)
43703             };
43704         };
43705         Bezier.prototype.length = function () {
43706             var steps = 10;
43707             var length = 0;
43708             var px;
43709             var py;
43710             for (var i = 0; i <= steps; i += 1) {
43711                 var t = i / steps;
43712                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43713                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43714                 if (i > 0) {
43715                     var xdiff = cx - px;
43716                     var ydiff = cy - py;
43717                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43718                 }
43719                 px = cx;
43720                 py = cy;
43721             }
43722             return length;
43723         };
43724         Bezier.prototype.point = function (t, start, c1, c2, end) {
43725             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43726             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43727             + (3.0 * c2 * (1.0 - t) * t * t)
43728             + (end * t * t * t);
43729         };
43730         return Bezier;
43731     }()),
43732     
43733     throttleStroke: function(fn, wait) {
43734       if (wait === void 0) { wait = 250; }
43735       var previous = 0;
43736       var timeout = null;
43737       var result;
43738       var storedContext;
43739       var storedArgs;
43740       var later = function () {
43741           previous = Date.now();
43742           timeout = null;
43743           result = fn.apply(storedContext, storedArgs);
43744           if (!timeout) {
43745               storedContext = null;
43746               storedArgs = [];
43747           }
43748       };
43749       return function wrapper() {
43750           var args = [];
43751           for (var _i = 0; _i < arguments.length; _i++) {
43752               args[_i] = arguments[_i];
43753           }
43754           var now = Date.now();
43755           var remaining = wait - (now - previous);
43756           storedContext = this;
43757           storedArgs = args;
43758           if (remaining <= 0 || remaining > wait) {
43759               if (timeout) {
43760                   clearTimeout(timeout);
43761                   timeout = null;
43762               }
43763               previous = now;
43764               result = fn.apply(storedContext, storedArgs);
43765               if (!timeout) {
43766                   storedContext = null;
43767                   storedArgs = [];
43768               }
43769           }
43770           else if (!timeout) {
43771               timeout = window.setTimeout(later, remaining);
43772           }
43773           return result;
43774       };
43775   }
43776   
43777 });
43778
43779  
43780
43781