43aa293fb711c7f9a9e7788a8a68b08fa9a2873d
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets, function(s) {
10                     if ( s.href  && s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })(); /*
16  * Based on:
17  * Ext JS Library 1.1.1
18  * Copyright(c) 2006-2007, Ext JS, LLC.
19  *
20  * Originally Released Under LGPL - original licence link has changed is not relivant.
21  *
22  * Fork - LGPL
23  * <script type="text/javascript">
24  */
25
26
27 /**
28  * @class Roo.Shadow
29  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
30  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
31  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
32  * @constructor
33  * Create a new Shadow
34  * @param {Object} config The config object
35  */
36 Roo.Shadow = function(config){
37     Roo.apply(this, config);
38     if(typeof this.mode != "string"){
39         this.mode = this.defaultMode;
40     }
41     var o = this.offset, a = {h: 0};
42     var rad = Math.floor(this.offset/2);
43     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
44         case "drop":
45             a.w = 0;
46             a.l = a.t = o;
47             a.t -= 1;
48             if(Roo.isIE){
49                 a.l -= this.offset + rad;
50                 a.t -= this.offset + rad;
51                 a.w -= rad;
52                 a.h -= rad;
53                 a.t += 1;
54             }
55         break;
56         case "sides":
57             a.w = (o*2);
58             a.l = -o;
59             a.t = o-1;
60             if(Roo.isIE){
61                 a.l -= (this.offset - rad);
62                 a.t -= this.offset + rad;
63                 a.l += 1;
64                 a.w -= (this.offset - rad)*2;
65                 a.w -= rad + 1;
66                 a.h -= 1;
67             }
68         break;
69         case "frame":
70             a.w = a.h = (o*2);
71             a.l = a.t = -o;
72             a.t += 1;
73             a.h -= 2;
74             if(Roo.isIE){
75                 a.l -= (this.offset - rad);
76                 a.t -= (this.offset - rad);
77                 a.l += 1;
78                 a.w -= (this.offset + rad + 1);
79                 a.h -= (this.offset + rad);
80                 a.h += 1;
81             }
82         break;
83     };
84
85     this.adjusts = a;
86 };
87
88 Roo.Shadow.prototype = {
89     /**
90      * @cfg {String} mode
91      * The shadow display mode.  Supports the following options:<br />
92      * sides: Shadow displays on both sides and bottom only<br />
93      * frame: Shadow displays equally on all four sides<br />
94      * drop: Traditional bottom-right drop shadow (default)
95      */
96     /**
97      * @cfg {String} offset
98      * The number of pixels to offset the shadow from the element (defaults to 4)
99      */
100     offset: 4,
101
102     // private
103     defaultMode: "drop",
104
105     /**
106      * Displays the shadow under the target element
107      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
108      */
109     show : function(target){
110         target = Roo.get(target);
111         if(!this.el){
112             this.el = Roo.Shadow.Pool.pull();
113             if(this.el.dom.nextSibling != target.dom){
114                 this.el.insertBefore(target);
115             }
116         }
117         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
118         if(Roo.isIE){
119             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
120         }
121         this.realign(
122             target.getLeft(true),
123             target.getTop(true),
124             target.getWidth(),
125             target.getHeight()
126         );
127         this.el.dom.style.display = "block";
128     },
129
130     /**
131      * Returns true if the shadow is visible, else false
132      */
133     isVisible : function(){
134         return this.el ? true : false;  
135     },
136
137     /**
138      * Direct alignment when values are already available. Show must be called at least once before
139      * calling this method to ensure it is initialized.
140      * @param {Number} left The target element left position
141      * @param {Number} top The target element top position
142      * @param {Number} width The target element width
143      * @param {Number} height The target element height
144      */
145     realign : function(l, t, w, h){
146         if(!this.el){
147             return;
148         }
149         var a = this.adjusts, d = this.el.dom, s = d.style;
150         var iea = 0;
151         s.left = (l+a.l)+"px";
152         s.top = (t+a.t)+"px";
153         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
154  
155         if(s.width != sws || s.height != shs){
156             s.width = sws;
157             s.height = shs;
158             if(!Roo.isIE){
159                 var cn = d.childNodes;
160                 var sww = Math.max(0, (sw-12))+"px";
161                 cn[0].childNodes[1].style.width = sww;
162                 cn[1].childNodes[1].style.width = sww;
163                 cn[2].childNodes[1].style.width = sww;
164                 cn[1].style.height = Math.max(0, (sh-12))+"px";
165             }
166         }
167     },
168
169     /**
170      * Hides this shadow
171      */
172     hide : function(){
173         if(this.el){
174             this.el.dom.style.display = "none";
175             Roo.Shadow.Pool.push(this.el);
176             delete this.el;
177         }
178     },
179
180     /**
181      * Adjust the z-index of this shadow
182      * @param {Number} zindex The new z-index
183      */
184     setZIndex : function(z){
185         this.zIndex = z;
186         if(this.el){
187             this.el.setStyle("z-index", z);
188         }
189     }
190 };
191
192 // Private utility class that manages the internal Shadow cache
193 Roo.Shadow.Pool = function(){
194     var p = [];
195     var markup = Roo.isIE ?
196                  '<div class="x-ie-shadow"></div>' :
197                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
198     return {
199         pull : function(){
200             var sh = p.shift();
201             if(!sh){
202                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
203                 sh.autoBoxAdjust = false;
204             }
205             return sh;
206         },
207
208         push : function(sh){
209             p.push(sh);
210         }
211     };
212 }();/*
213  * - LGPL
214  *
215  * base class for bootstrap elements.
216  * 
217  */
218
219 Roo.bootstrap = Roo.bootstrap || {};
220 /**
221  * @class Roo.bootstrap.Component
222  * @extends Roo.Component
223  * Bootstrap Component base class
224  * @cfg {String} cls css class
225  * @cfg {String} style any extra css
226  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
227  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
228  * @cfg {string} dataId cutomer id
229  * @cfg {string} name Specifies name attribute
230  * @cfg {string} tooltip  Text for the tooltip
231  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
232  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
233  
234  * @constructor
235  * Do not use directly - it does not do anything..
236  * @param {Object} config The config object
237  */
238
239
240
241 Roo.bootstrap.Component = function(config){
242     Roo.bootstrap.Component.superclass.constructor.call(this, config);
243        
244     this.addEvents({
245         /**
246          * @event childrenrendered
247          * Fires when the children have been rendered..
248          * @param {Roo.bootstrap.Component} this
249          */
250         "childrenrendered" : true
251         
252         
253         
254     });
255     
256     
257 };
258
259 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
260     
261     
262     allowDomMove : false, // to stop relocations in parent onRender...
263     
264     cls : false,
265     
266     style : false,
267     
268     autoCreate : false,
269     
270     tooltip : null,
271     /**
272      * Initialize Events for the element
273      */
274     initEvents : function() { },
275     
276     xattr : false,
277     
278     parentId : false,
279     
280     can_build_overlaid : true,
281     
282     container_method : false,
283     
284     dataId : false,
285     
286     name : false,
287     
288     parent: function() {
289         // returns the parent component..
290         return Roo.ComponentMgr.get(this.parentId)
291         
292         
293     },
294     
295     // private
296     onRender : function(ct, position)
297     {
298        // Roo.log("Call onRender: " + this.xtype);
299         
300         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
301         
302         if(this.el){
303             if (this.el.attr('xtype')) {
304                 this.el.attr('xtypex', this.el.attr('xtype'));
305                 this.el.dom.removeAttribute('xtype');
306                 
307                 this.initEvents();
308             }
309             
310             return;
311         }
312         
313          
314         
315         var cfg = Roo.apply({},  this.getAutoCreate());
316         
317         cfg.id = this.id || Roo.id();
318         
319         // fill in the extra attributes 
320         if (this.xattr && typeof(this.xattr) =='object') {
321             for (var i in this.xattr) {
322                 cfg[i] = this.xattr[i];
323             }
324         }
325         
326         if(this.dataId){
327             cfg.dataId = this.dataId;
328         }
329         
330         if (this.cls) {
331             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
332         }
333         
334         if (this.style) { // fixme needs to support more complex style data.
335             cfg.style = this.style;
336         }
337         
338         if(this.name){
339             cfg.name = this.name;
340         }
341         
342         this.el = ct.createChild(cfg, position);
343         
344         if (this.tooltip) {
345             this.tooltipEl().attr('tooltip', this.tooltip);
346         }
347         
348         if(this.tabIndex !== undefined){
349             this.el.dom.setAttribute('tabIndex', this.tabIndex);
350         }
351         
352         this.initEvents();
353         
354     },
355     /**
356      * Fetch the element to add children to
357      * @return {Roo.Element} defaults to this.el
358      */
359     getChildContainer : function()
360     {
361         return this.el;
362     },
363     /**
364      * Fetch the element to display the tooltip on.
365      * @return {Roo.Element} defaults to this.el
366      */
367     tooltipEl : function()
368     {
369         return this.el;
370     },
371         
372     addxtype  : function(tree,cntr)
373     {
374         var cn = this;
375         
376         cn = Roo.factory(tree);
377         //Roo.log(['addxtype', cn]);
378            
379         cn.parentType = this.xtype; //??
380         cn.parentId = this.id;
381         
382         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
383         if (typeof(cn.container_method) == 'string') {
384             cntr = cn.container_method;
385         }
386         
387         
388         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
389         
390         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
391         
392         var build_from_html =  Roo.XComponent.build_from_html;
393           
394         var is_body  = (tree.xtype == 'Body') ;
395           
396         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
397           
398         var self_cntr_el = Roo.get(this[cntr](false));
399         
400         // do not try and build conditional elements 
401         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
402             return false;
403         }
404         
405         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
406             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
407                 return this.addxtypeChild(tree,cntr, is_body);
408             }
409             
410             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
411                 
412             if(echild){
413                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
414             }
415             
416             Roo.log('skipping render');
417             return cn;
418             
419         }
420         
421         var ret = false;
422         if (!build_from_html) {
423             return false;
424         }
425         
426         // this i think handles overlaying multiple children of the same type
427         // with the sam eelement.. - which might be buggy..
428         while (true) {
429             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
430             
431             if (!echild) {
432                 break;
433             }
434             
435             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
436                 break;
437             }
438             
439             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
440         }
441        
442         return ret;
443     },
444     
445     
446     addxtypeChild : function (tree, cntr, is_body)
447     {
448         Roo.debug && Roo.log('addxtypeChild:' + cntr);
449         var cn = this;
450         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
451         
452         
453         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
454                     (typeof(tree['flexy:foreach']) != 'undefined');
455           
456     
457         
458         skip_children = false;
459         // render the element if it's not BODY.
460         if (!is_body) {
461             
462             // if parent was disabled, then do not try and create the children..
463             if(!this[cntr](true)){
464                 tree.items = [];
465                 return tree;
466             }
467            
468             cn = Roo.factory(tree);
469            
470             cn.parentType = this.xtype; //??
471             cn.parentId = this.id;
472             
473             var build_from_html =  Roo.XComponent.build_from_html;
474             
475             
476             // does the container contain child eleemnts with 'xtype' attributes.
477             // that match this xtype..
478             // note - when we render we create these as well..
479             // so we should check to see if body has xtype set.
480             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
481                
482                 var self_cntr_el = Roo.get(this[cntr](false));
483                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
484                 if (echild) { 
485                     //Roo.log(Roo.XComponent.build_from_html);
486                     //Roo.log("got echild:");
487                     //Roo.log(echild);
488                 }
489                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
490                 // and are not displayed -this causes this to use up the wrong element when matching.
491                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
492                 
493                 
494                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
495                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
496                   
497                   
498                   
499                     cn.el = echild;
500                   //  Roo.log("GOT");
501                     //echild.dom.removeAttribute('xtype');
502                 } else {
503                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
504                     Roo.debug && Roo.log(self_cntr_el);
505                     Roo.debug && Roo.log(echild);
506                     Roo.debug && Roo.log(cn);
507                 }
508             }
509            
510             
511            
512             // if object has flexy:if - then it may or may not be rendered.
513             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
514                 // skip a flexy if element.
515                 Roo.debug && Roo.log('skipping render');
516                 Roo.debug && Roo.log(tree);
517                 if (!cn.el) {
518                     Roo.debug && Roo.log('skipping all children');
519                     skip_children = true;
520                 }
521                 
522              } else {
523                  
524                 // actually if flexy:foreach is found, we really want to create 
525                 // multiple copies here...
526                 //Roo.log('render');
527                 //Roo.log(this[cntr]());
528                 // some elements do not have render methods.. like the layouts...
529                 /*
530                 if(this[cntr](true) === false){
531                     cn.items = [];
532                     return cn;
533                 }
534                 */
535                 cn.render && cn.render(this[cntr](true));
536                 
537              }
538             // then add the element..
539         }
540          
541         // handle the kids..
542         
543         var nitems = [];
544         /*
545         if (typeof (tree.menu) != 'undefined') {
546             tree.menu.parentType = cn.xtype;
547             tree.menu.triggerEl = cn.el;
548             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
549             
550         }
551         */
552         if (!tree.items || !tree.items.length) {
553             cn.items = nitems;
554             //Roo.log(["no children", this]);
555             
556             return cn;
557         }
558          
559         var items = tree.items;
560         delete tree.items;
561         
562         //Roo.log(items.length);
563             // add the items..
564         if (!skip_children) {    
565             for(var i =0;i < items.length;i++) {
566               //  Roo.log(['add child', items[i]]);
567                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
568             }
569         }
570         
571         cn.items = nitems;
572         
573         //Roo.log("fire childrenrendered");
574         
575         cn.fireEvent('childrenrendered', this);
576         
577         return cn;
578     },
579     
580     /**
581      * Set the element that will be used to show or hide
582      */
583     setVisibilityEl : function(el)
584     {
585         this.visibilityEl = el;
586     },
587     
588      /**
589      * Get the element that will be used to show or hide
590      */
591     getVisibilityEl : function()
592     {
593         if (typeof(this.visibilityEl) == 'object') {
594             return this.visibilityEl;
595         }
596         
597         if (typeof(this.visibilityEl) == 'string') {
598             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
599         }
600         
601         return this.getEl();
602     },
603     
604     /**
605      * Show a component - removes 'hidden' class
606      */
607     show : function()
608     {
609         if(!this.getVisibilityEl()){
610             return;
611         }
612          
613         this.getVisibilityEl().removeClass(['hidden','d-none']);
614         
615         this.fireEvent('show', this);
616         
617         
618     },
619     /**
620      * Hide a component - adds 'hidden' class
621      */
622     hide: function()
623     {
624         if(!this.getVisibilityEl()){
625             return;
626         }
627         
628         this.getVisibilityEl().addClass(['hidden','d-none']);
629         
630         this.fireEvent('hide', this);
631         
632     }
633 });
634
635  /*
636  * - LGPL
637  *
638  * element
639  * 
640  */
641
642 /**
643  * @class Roo.bootstrap.Element
644  * @extends Roo.bootstrap.Component
645  * Bootstrap Element class
646  * @cfg {String} html contents of the element
647  * @cfg {String} tag tag of the element
648  * @cfg {String} cls class of the element
649  * @cfg {Boolean} preventDefault (true|false) default false
650  * @cfg {Boolean} clickable (true|false) default false
651  * 
652  * @constructor
653  * Create a new Element
654  * @param {Object} config The config object
655  */
656
657 Roo.bootstrap.Element = function(config){
658     Roo.bootstrap.Element.superclass.constructor.call(this, config);
659     
660     this.addEvents({
661         // raw events
662         /**
663          * @event click
664          * When a element is chick
665          * @param {Roo.bootstrap.Element} this
666          * @param {Roo.EventObject} e
667          */
668         "click" : true
669     });
670 };
671
672 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
673     
674     tag: 'div',
675     cls: '',
676     html: '',
677     preventDefault: false, 
678     clickable: false,
679     
680     getAutoCreate : function(){
681         
682         var cfg = {
683             tag: this.tag,
684             // cls: this.cls, double assign in parent class Component.js :: onRender
685             html: this.html
686         };
687         
688         return cfg;
689     },
690     
691     initEvents: function() 
692     {
693         Roo.bootstrap.Element.superclass.initEvents.call(this);
694         
695         if(this.clickable){
696             this.el.on('click', this.onClick, this);
697         }
698         
699     },
700     
701     onClick : function(e)
702     {
703         if(this.preventDefault){
704             e.preventDefault();
705         }
706         
707         this.fireEvent('click', this, e);
708     },
709     
710     getValue : function()
711     {
712         return this.el.dom.innerHTML;
713     },
714     
715     setValue : function(value)
716     {
717         this.el.dom.innerHTML = value;
718     }
719    
720 });
721
722  
723
724  /*
725  * - LGPL
726  *
727  * dropable area
728  * 
729  */
730
731 /**
732  * @class Roo.bootstrap.DropTarget
733  * @extends Roo.bootstrap.Element
734  * Bootstrap DropTarget class
735  
736  * @cfg {string} name dropable name
737  * 
738  * @constructor
739  * Create a new Dropable Area
740  * @param {Object} config The config object
741  */
742
743 Roo.bootstrap.DropTarget = function(config){
744     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
745     
746     this.addEvents({
747         // raw events
748         /**
749          * @event click
750          * When a element is chick
751          * @param {Roo.bootstrap.Element} this
752          * @param {Roo.EventObject} e
753          */
754         "drop" : true
755     });
756 };
757
758 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
759     
760     
761     getAutoCreate : function(){
762         
763          
764     },
765     
766     initEvents: function() 
767     {
768         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
769         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
770             ddGroup: this.name,
771             listeners : {
772                 drop : this.dragDrop.createDelegate(this),
773                 enter : this.dragEnter.createDelegate(this),
774                 out : this.dragOut.createDelegate(this),
775                 over : this.dragOver.createDelegate(this)
776             }
777             
778         });
779         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
780     },
781     
782     dragDrop : function(source,e,data)
783     {
784         // user has to decide how to impliment this.
785         Roo.log('drop');
786         Roo.log(this);
787         //this.fireEvent('drop', this, source, e ,data);
788         return false;
789     },
790     
791     dragEnter : function(n, dd, e, data)
792     {
793         // probably want to resize the element to match the dropped element..
794         Roo.log("enter");
795         this.originalSize = this.el.getSize();
796         this.el.setSize( n.el.getSize());
797         this.dropZone.DDM.refreshCache(this.name);
798         Roo.log([n, dd, e, data]);
799     },
800     
801     dragOut : function(value)
802     {
803         // resize back to normal
804         Roo.log("out");
805         this.el.setSize(this.originalSize);
806         this.dropZone.resetConstraints();
807     },
808     
809     dragOver : function()
810     {
811         // ??? do nothing?
812     }
813    
814 });
815
816  
817
818  /*
819  * - LGPL
820  *
821  * Body
822  *
823  */
824
825 /**
826  * @class Roo.bootstrap.Body
827  * @extends Roo.bootstrap.Component
828  * Bootstrap Body class
829  *
830  * @constructor
831  * Create a new body
832  * @param {Object} config The config object
833  */
834
835 Roo.bootstrap.Body = function(config){
836
837     config = config || {};
838
839     Roo.bootstrap.Body.superclass.constructor.call(this, config);
840     this.el = Roo.get(config.el ? config.el : document.body );
841     if (this.cls && this.cls.length) {
842         Roo.get(document.body).addClass(this.cls);
843     }
844 };
845
846 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
847
848     is_body : true,// just to make sure it's constructed?
849
850         autoCreate : {
851         cls: 'container'
852     },
853     onRender : function(ct, position)
854     {
855        /* Roo.log("Roo.bootstrap.Body - onRender");
856         if (this.cls && this.cls.length) {
857             Roo.get(document.body).addClass(this.cls);
858         }
859         // style??? xttr???
860         */
861     }
862
863
864
865
866 });
867 /*
868  * - LGPL
869  *
870  * button group
871  * 
872  */
873
874
875 /**
876  * @class Roo.bootstrap.ButtonGroup
877  * @extends Roo.bootstrap.Component
878  * Bootstrap ButtonGroup class
879  * @cfg {String} size lg | sm | xs (default empty normal)
880  * @cfg {String} align vertical | justified  (default none)
881  * @cfg {String} direction up | down (default down)
882  * @cfg {Boolean} toolbar false | true
883  * @cfg {Boolean} btn true | false
884  * 
885  * 
886  * @constructor
887  * Create a new Input
888  * @param {Object} config The config object
889  */
890
891 Roo.bootstrap.ButtonGroup = function(config){
892     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
893 };
894
895 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
896     
897     size: '',
898     align: '',
899     direction: '',
900     toolbar: false,
901     btn: true,
902
903     getAutoCreate : function(){
904         var cfg = {
905             cls: 'btn-group',
906             html : null
907         };
908         
909         cfg.html = this.html || cfg.html;
910         
911         if (this.toolbar) {
912             cfg = {
913                 cls: 'btn-toolbar',
914                 html: null
915             };
916             
917             return cfg;
918         }
919         
920         if (['vertical','justified'].indexOf(this.align)!==-1) {
921             cfg.cls = 'btn-group-' + this.align;
922             
923             if (this.align == 'justified') {
924                 console.log(this.items);
925             }
926         }
927         
928         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
929             cfg.cls += ' btn-group-' + this.size;
930         }
931         
932         if (this.direction == 'up') {
933             cfg.cls += ' dropup' ;
934         }
935         
936         return cfg;
937     },
938     /**
939      * Add a button to the group (similar to NavItem API.)
940      */
941     addItem : function(cfg)
942     {
943         var cn = new Roo.bootstrap.Button(cfg);
944         //this.register(cn);
945         cn.parentId = this.id;
946         cn.onRender(this.el, null);
947         return cn;
948     }
949    
950 });
951
952  /*
953  * - LGPL
954  *
955  * button
956  * 
957  */
958
959 /**
960  * @class Roo.bootstrap.Button
961  * @extends Roo.bootstrap.Component
962  * Bootstrap Button class
963  * @cfg {String} html The button content
964  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link ) default
965  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link ) default (same as button)
966  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
967  * @cfg {String} size (lg|sm|xs)
968  * @cfg {String} tag (a|input|submit)
969  * @cfg {String} href empty or href
970  * @cfg {Boolean} disabled default false;
971  * @cfg {Boolean} isClose default false;
972  * @cfg {String} glyphicon depricated - use fa
973  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
974  * @cfg {String} badge text for badge
975  * @cfg {String} theme (default|glow)  
976  * @cfg {Boolean} inverse dark themed version
977  * @cfg {Boolean} toggle is it a slidy toggle button
978  * @cfg {Boolean} pressed   default null - if the button ahs active state
979  * @cfg {String} ontext text for on slidy toggle state
980  * @cfg {String} offtext text for off slidy toggle state
981  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
982  * @cfg {Boolean} removeClass remove the standard class..
983  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
984  * 
985  * @constructor
986  * Create a new button
987  * @param {Object} config The config object
988  */
989
990
991 Roo.bootstrap.Button = function(config){
992     Roo.bootstrap.Button.superclass.constructor.call(this, config);
993     this.weightClass = ["btn-default btn-outline-secondary", 
994                        "btn-primary", 
995                        "btn-success", 
996                        "btn-info", 
997                        "btn-warning",
998                        "btn-danger",
999                        "btn-link"
1000                       ],  
1001     this.addEvents({
1002         // raw events
1003         /**
1004          * @event click
1005          * When a butotn is pressed
1006          * @param {Roo.bootstrap.Button} btn
1007          * @param {Roo.EventObject} e
1008          */
1009         "click" : true,
1010          /**
1011          * @event toggle
1012          * After the button has been toggles
1013          * @param {Roo.bootstrap.Button} btn
1014          * @param {Roo.EventObject} e
1015          * @param {boolean} pressed (also available as button.pressed)
1016          */
1017         "toggle" : true
1018     });
1019 };
1020
1021 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1022     html: false,
1023     active: false,
1024     weight: '',
1025     badge_weight: '',
1026     outline : false,
1027     size: '',
1028     tag: 'button',
1029     href: '',
1030     disabled: false,
1031     isClose: false,
1032     glyphicon: '',
1033     fa: '',
1034     badge: '',
1035     theme: 'default',
1036     inverse: false,
1037     
1038     toggle: false,
1039     ontext: 'ON',
1040     offtext: 'OFF',
1041     defaulton: true,
1042     preventDefault: true,
1043     removeClass: false,
1044     name: false,
1045     target: false,
1046      
1047     pressed : null,
1048      
1049     
1050     getAutoCreate : function(){
1051         
1052         var cfg = {
1053             tag : 'button',
1054             cls : 'roo-button',
1055             html: ''
1056         };
1057         
1058         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1059             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1060             this.tag = 'button';
1061         } else {
1062             cfg.tag = this.tag;
1063         }
1064         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1065         
1066         if (this.toggle == true) {
1067             cfg={
1068                 tag: 'div',
1069                 cls: 'slider-frame roo-button',
1070                 cn: [
1071                     {
1072                         tag: 'span',
1073                         'data-on-text':'ON',
1074                         'data-off-text':'OFF',
1075                         cls: 'slider-button',
1076                         html: this.offtext
1077                     }
1078                 ]
1079             };
1080             
1081             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
1082                 cfg.cls += ' '+this.weight;
1083             }
1084             
1085             return cfg;
1086         }
1087         
1088         if (this.isClose) {
1089             cfg.cls += ' close';
1090             
1091             cfg["aria-hidden"] = true;
1092             
1093             cfg.html = "&times;";
1094             
1095             return cfg;
1096         }
1097         
1098          
1099         if (this.theme==='default') {
1100             cfg.cls = 'btn roo-button';
1101             
1102             //if (this.parentType != 'Navbar') {
1103             this.weight = this.weight.length ?  this.weight : 'default';
1104             //}
1105             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
1106                 
1107                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1108                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1109                 cfg.cls += ' btn-' + outline + weight;
1110                 if (this.weight == 'default') {
1111                     // BC
1112                     cfg.cls += ' btn-' + this.weight;
1113                 }
1114             }
1115         } else if (this.theme==='glow') {
1116             
1117             cfg.tag = 'a';
1118             cfg.cls = 'btn-glow roo-button';
1119             
1120             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
1121                 
1122                 cfg.cls += ' ' + this.weight;
1123             }
1124         }
1125    
1126         
1127         if (this.inverse) {
1128             this.cls += ' inverse';
1129         }
1130         
1131         
1132         if (this.active || this.pressed === true) {
1133             cfg.cls += ' active';
1134         }
1135         
1136         if (this.disabled) {
1137             cfg.disabled = 'disabled';
1138         }
1139         
1140         if (this.items) {
1141             Roo.log('changing to ul' );
1142             cfg.tag = 'ul';
1143             this.glyphicon = 'caret';
1144             if (Roo.bootstrap.version == 4) {
1145                 this.fa = 'caret-down';
1146             }
1147             
1148         }
1149         
1150         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1151          
1152         //gsRoo.log(this.parentType);
1153         if (this.parentType === 'Navbar' && !this.parent().bar) {
1154             Roo.log('changing to li?');
1155             
1156             cfg.tag = 'li';
1157             
1158             cfg.cls = '';
1159             cfg.cn =  [{
1160                 tag : 'a',
1161                 cls : 'roo-button',
1162                 html : this.html,
1163                 href : this.href || '#'
1164             }];
1165             if (this.menu) {
1166                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1167                 cfg.cls += ' dropdown';
1168             }   
1169             
1170             delete cfg.html;
1171             
1172         }
1173         
1174        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1175         
1176         if (this.glyphicon) {
1177             cfg.html = ' ' + cfg.html;
1178             
1179             cfg.cn = [
1180                 {
1181                     tag: 'span',
1182                     cls: 'glyphicon glyphicon-' + this.glyphicon
1183                 }
1184             ];
1185         }
1186         if (this.fa) {
1187             cfg.html = ' ' + cfg.html;
1188             
1189             cfg.cn = [
1190                 {
1191                     tag: 'i',
1192                     cls: 'fa fas fa-' + this.fa
1193                 }
1194             ];
1195         }
1196         
1197         if (this.badge) {
1198             cfg.html += ' ';
1199             
1200             cfg.tag = 'a';
1201             
1202 //            cfg.cls='btn roo-button';
1203             
1204             cfg.href=this.href;
1205             
1206             var value = cfg.html;
1207             
1208             if(this.glyphicon){
1209                 value = {
1210                     tag: 'span',
1211                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1212                     html: this.html
1213                 };
1214             }
1215             if(this.fa){
1216                 value = {
1217                     tag: 'i',
1218                     cls: 'fa fas fa-' + this.fa,
1219                     html: this.html
1220                 };
1221             }
1222             
1223             var bw = this.badge_weight.length ? this.badge_weight :
1224                 (this.weight.length ? this.weight : 'secondary');
1225             bw = bw == 'default' ? 'secondary' : bw;
1226             
1227             cfg.cn = [
1228                 value,
1229                 {
1230                     tag: 'span',
1231                     cls: 'badge badge-' + bw,
1232                     html: this.badge
1233                 }
1234             ];
1235             
1236             cfg.html='';
1237         }
1238         
1239         if (this.menu) {
1240             cfg.cls += ' dropdown';
1241             cfg.html = typeof(cfg.html) != 'undefined' ?
1242                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1243         }
1244         
1245         if (cfg.tag !== 'a' && this.href !== '') {
1246             throw "Tag must be a to set href.";
1247         } else if (this.href.length > 0) {
1248             cfg.href = this.href;
1249         }
1250         
1251         if(this.removeClass){
1252             cfg.cls = '';
1253         }
1254         
1255         if(this.target){
1256             cfg.target = this.target;
1257         }
1258         
1259         return cfg;
1260     },
1261     initEvents: function() {
1262        // Roo.log('init events?');
1263 //        Roo.log(this.el.dom);
1264         // add the menu...
1265         
1266         if (typeof (this.menu) != 'undefined') {
1267             this.menu.parentType = this.xtype;
1268             this.menu.triggerEl = this.el;
1269             this.addxtype(Roo.apply({}, this.menu));
1270         }
1271
1272
1273        if (this.el.hasClass('roo-button')) {
1274             this.el.on('click', this.onClick, this);
1275        } else {
1276             this.el.select('.roo-button').on('click', this.onClick, this);
1277        }
1278        
1279        if(this.removeClass){
1280            this.el.on('click', this.onClick, this);
1281        }
1282        
1283        this.el.enableDisplayMode();
1284         
1285     },
1286     onClick : function(e)
1287     {
1288         if (this.disabled) {
1289             return;
1290         }
1291         
1292         Roo.log('button on click ');
1293         if(this.preventDefault){
1294             e.preventDefault();
1295         }
1296         
1297         if (this.pressed === true || this.pressed === false) {
1298             this.toggleActive(e);
1299         }
1300         
1301         
1302         this.fireEvent('click', this, e);
1303     },
1304     
1305     /**
1306      * Enables this button
1307      */
1308     enable : function()
1309     {
1310         this.disabled = false;
1311         this.el.removeClass('disabled');
1312     },
1313     
1314     /**
1315      * Disable this button
1316      */
1317     disable : function()
1318     {
1319         this.disabled = true;
1320         this.el.addClass('disabled');
1321     },
1322      /**
1323      * sets the active state on/off, 
1324      * @param {Boolean} state (optional) Force a particular state
1325      */
1326     setActive : function(v) {
1327         
1328         this.el[v ? 'addClass' : 'removeClass']('active');
1329         this.pressed = v;
1330     },
1331      /**
1332      * toggles the current active state 
1333      */
1334     toggleActive : function(e)
1335     {
1336         this.setActive(!this.pressed);
1337         this.fireEvent('toggle', this, e, !this.pressed);
1338     },
1339      /**
1340      * get the current active state
1341      * @return {boolean} true if it's active
1342      */
1343     isActive : function()
1344     {
1345         return this.el.hasClass('active');
1346     },
1347     /**
1348      * set the text of the first selected button
1349      */
1350     setText : function(str)
1351     {
1352         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1353     },
1354     /**
1355      * get the text of the first selected button
1356      */
1357     getText : function()
1358     {
1359         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1360     },
1361     
1362     setWeight : function(str)
1363     {
1364         this.el.removeClass(this.weightClass);
1365         this.weight = str;
1366         var outline = this.outline ? 'outline-' : '';
1367         if (str == 'default') {
1368             this.el.addClass('btn-default btn-outline-secondary');        
1369             return;
1370         }
1371         this.el.addClass('btn-' + outline + str);        
1372     }
1373     
1374     
1375 });
1376
1377  /*
1378  * - LGPL
1379  *
1380  * column
1381  * 
1382  */
1383
1384 /**
1385  * @class Roo.bootstrap.Column
1386  * @extends Roo.bootstrap.Component
1387  * Bootstrap Column class
1388  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1389  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1390  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1391  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1392  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1393  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1394  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1395  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1396  *
1397  * 
1398  * @cfg {Boolean} hidden (true|false) hide the element
1399  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1400  * @cfg {String} fa (ban|check|...) font awesome icon
1401  * @cfg {Number} fasize (1|2|....) font awsome size
1402
1403  * @cfg {String} icon (info-sign|check|...) glyphicon name
1404
1405  * @cfg {String} html content of column.
1406  * 
1407  * @constructor
1408  * Create a new Column
1409  * @param {Object} config The config object
1410  */
1411
1412 Roo.bootstrap.Column = function(config){
1413     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1414 };
1415
1416 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1417     
1418     xs: false,
1419     sm: false,
1420     md: false,
1421     lg: false,
1422     xsoff: false,
1423     smoff: false,
1424     mdoff: false,
1425     lgoff: false,
1426     html: '',
1427     offset: 0,
1428     alert: false,
1429     fa: false,
1430     icon : false,
1431     hidden : false,
1432     fasize : 1,
1433     
1434     getAutoCreate : function(){
1435         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1436         
1437         cfg = {
1438             tag: 'div',
1439             cls: 'column'
1440         };
1441         
1442         var settings=this;
1443         var sizes =   ['xs','sm','md','lg'];
1444         sizes.map(function(size ,ix){
1445             //Roo.log( size + ':' + settings[size]);
1446             
1447             if (settings[size+'off'] !== false) {
1448                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1449             }
1450             
1451             if (settings[size] === false) {
1452                 return;
1453             }
1454             
1455             if (!settings[size]) { // 0 = hidden
1456                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1457                 // bootsrap4
1458                 for (var i = ix; i > -1; i--) {
1459                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1460                 }
1461                 
1462                 
1463                 return;
1464             }
1465             cfg.cls += ' col-' + size + '-' + settings[size] + (
1466                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1467             );
1468             
1469         });
1470         
1471         if (this.hidden) {
1472             cfg.cls += ' hidden';
1473         }
1474         
1475         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1476             cfg.cls +=' alert alert-' + this.alert;
1477         }
1478         
1479         
1480         if (this.html.length) {
1481             cfg.html = this.html;
1482         }
1483         if (this.fa) {
1484             var fasize = '';
1485             if (this.fasize > 1) {
1486                 fasize = ' fa-' + this.fasize + 'x';
1487             }
1488             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1489             
1490             
1491         }
1492         if (this.icon) {
1493             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1494         }
1495         
1496         return cfg;
1497     }
1498    
1499 });
1500
1501  
1502
1503  /*
1504  * - LGPL
1505  *
1506  * page container.
1507  * 
1508  */
1509
1510
1511 /**
1512  * @class Roo.bootstrap.Container
1513  * @extends Roo.bootstrap.Component
1514  * Bootstrap Container class
1515  * @cfg {Boolean} jumbotron is it a jumbotron element
1516  * @cfg {String} html content of element
1517  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1518  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1519  * @cfg {String} header content of header (for panel)
1520  * @cfg {String} footer content of footer (for panel)
1521  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1522  * @cfg {String} tag (header|aside|section) type of HTML tag.
1523  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1524  * @cfg {String} fa font awesome icon
1525  * @cfg {String} icon (info-sign|check|...) glyphicon name
1526  * @cfg {Boolean} hidden (true|false) hide the element
1527  * @cfg {Boolean} expandable (true|false) default false
1528  * @cfg {Boolean} expanded (true|false) default true
1529  * @cfg {String} rheader contet on the right of header
1530  * @cfg {Boolean} clickable (true|false) default false
1531
1532  *     
1533  * @constructor
1534  * Create a new Container
1535  * @param {Object} config The config object
1536  */
1537
1538 Roo.bootstrap.Container = function(config){
1539     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1540     
1541     this.addEvents({
1542         // raw events
1543          /**
1544          * @event expand
1545          * After the panel has been expand
1546          * 
1547          * @param {Roo.bootstrap.Container} this
1548          */
1549         "expand" : true,
1550         /**
1551          * @event collapse
1552          * After the panel has been collapsed
1553          * 
1554          * @param {Roo.bootstrap.Container} this
1555          */
1556         "collapse" : true,
1557         /**
1558          * @event click
1559          * When a element is chick
1560          * @param {Roo.bootstrap.Container} this
1561          * @param {Roo.EventObject} e
1562          */
1563         "click" : true
1564     });
1565 };
1566
1567 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1568     
1569     jumbotron : false,
1570     well: '',
1571     panel : '',
1572     header: '',
1573     footer : '',
1574     sticky: '',
1575     tag : false,
1576     alert : false,
1577     fa: false,
1578     icon : false,
1579     expandable : false,
1580     rheader : '',
1581     expanded : true,
1582     clickable: false,
1583   
1584      
1585     getChildContainer : function() {
1586         
1587         if(!this.el){
1588             return false;
1589         }
1590         
1591         if (this.panel.length) {
1592             return this.el.select('.panel-body',true).first();
1593         }
1594         
1595         return this.el;
1596     },
1597     
1598     
1599     getAutoCreate : function(){
1600         
1601         var cfg = {
1602             tag : this.tag || 'div',
1603             html : '',
1604             cls : ''
1605         };
1606         if (this.jumbotron) {
1607             cfg.cls = 'jumbotron';
1608         }
1609         
1610         
1611         
1612         // - this is applied by the parent..
1613         //if (this.cls) {
1614         //    cfg.cls = this.cls + '';
1615         //}
1616         
1617         if (this.sticky.length) {
1618             
1619             var bd = Roo.get(document.body);
1620             if (!bd.hasClass('bootstrap-sticky')) {
1621                 bd.addClass('bootstrap-sticky');
1622                 Roo.select('html',true).setStyle('height', '100%');
1623             }
1624              
1625             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1626         }
1627         
1628         
1629         if (this.well.length) {
1630             switch (this.well) {
1631                 case 'lg':
1632                 case 'sm':
1633                     cfg.cls +=' well well-' +this.well;
1634                     break;
1635                 default:
1636                     cfg.cls +=' well';
1637                     break;
1638             }
1639         }
1640         
1641         if (this.hidden) {
1642             cfg.cls += ' hidden';
1643         }
1644         
1645         
1646         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1647             cfg.cls +=' alert alert-' + this.alert;
1648         }
1649         
1650         var body = cfg;
1651         
1652         if (this.panel.length) {
1653             cfg.cls += ' panel panel-' + this.panel;
1654             cfg.cn = [];
1655             if (this.header.length) {
1656                 
1657                 var h = [];
1658                 
1659                 if(this.expandable){
1660                     
1661                     cfg.cls = cfg.cls + ' expandable';
1662                     
1663                     h.push({
1664                         tag: 'i',
1665                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1666                     });
1667                     
1668                 }
1669                 
1670                 h.push(
1671                     {
1672                         tag: 'span',
1673                         cls : 'panel-title',
1674                         html : (this.expandable ? '&nbsp;' : '') + this.header
1675                     },
1676                     {
1677                         tag: 'span',
1678                         cls: 'panel-header-right',
1679                         html: this.rheader
1680                     }
1681                 );
1682                 
1683                 cfg.cn.push({
1684                     cls : 'panel-heading',
1685                     style : this.expandable ? 'cursor: pointer' : '',
1686                     cn : h
1687                 });
1688                 
1689             }
1690             
1691             body = false;
1692             cfg.cn.push({
1693                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1694                 html : this.html
1695             });
1696             
1697             
1698             if (this.footer.length) {
1699                 cfg.cn.push({
1700                     cls : 'panel-footer',
1701                     html : this.footer
1702                     
1703                 });
1704             }
1705             
1706         }
1707         
1708         if (body) {
1709             body.html = this.html || cfg.html;
1710             // prefix with the icons..
1711             if (this.fa) {
1712                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1713             }
1714             if (this.icon) {
1715                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1716             }
1717             
1718             
1719         }
1720         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1721             cfg.cls =  'container';
1722         }
1723         
1724         return cfg;
1725     },
1726     
1727     initEvents: function() 
1728     {
1729         if(this.expandable){
1730             var headerEl = this.headerEl();
1731         
1732             if(headerEl){
1733                 headerEl.on('click', this.onToggleClick, this);
1734             }
1735         }
1736         
1737         if(this.clickable){
1738             this.el.on('click', this.onClick, this);
1739         }
1740         
1741     },
1742     
1743     onToggleClick : function()
1744     {
1745         var headerEl = this.headerEl();
1746         
1747         if(!headerEl){
1748             return;
1749         }
1750         
1751         if(this.expanded){
1752             this.collapse();
1753             return;
1754         }
1755         
1756         this.expand();
1757     },
1758     
1759     expand : function()
1760     {
1761         if(this.fireEvent('expand', this)) {
1762             
1763             this.expanded = true;
1764             
1765             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1766             
1767             this.el.select('.panel-body',true).first().removeClass('hide');
1768             
1769             var toggleEl = this.toggleEl();
1770
1771             if(!toggleEl){
1772                 return;
1773             }
1774
1775             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1776         }
1777         
1778     },
1779     
1780     collapse : function()
1781     {
1782         if(this.fireEvent('collapse', this)) {
1783             
1784             this.expanded = false;
1785             
1786             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1787             this.el.select('.panel-body',true).first().addClass('hide');
1788         
1789             var toggleEl = this.toggleEl();
1790
1791             if(!toggleEl){
1792                 return;
1793             }
1794
1795             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1796         }
1797     },
1798     
1799     toggleEl : function()
1800     {
1801         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1802             return;
1803         }
1804         
1805         return this.el.select('.panel-heading .fa',true).first();
1806     },
1807     
1808     headerEl : function()
1809     {
1810         if(!this.el || !this.panel.length || !this.header.length){
1811             return;
1812         }
1813         
1814         return this.el.select('.panel-heading',true).first()
1815     },
1816     
1817     bodyEl : function()
1818     {
1819         if(!this.el || !this.panel.length){
1820             return;
1821         }
1822         
1823         return this.el.select('.panel-body',true).first()
1824     },
1825     
1826     titleEl : function()
1827     {
1828         if(!this.el || !this.panel.length || !this.header.length){
1829             return;
1830         }
1831         
1832         return this.el.select('.panel-title',true).first();
1833     },
1834     
1835     setTitle : function(v)
1836     {
1837         var titleEl = this.titleEl();
1838         
1839         if(!titleEl){
1840             return;
1841         }
1842         
1843         titleEl.dom.innerHTML = v;
1844     },
1845     
1846     getTitle : function()
1847     {
1848         
1849         var titleEl = this.titleEl();
1850         
1851         if(!titleEl){
1852             return '';
1853         }
1854         
1855         return titleEl.dom.innerHTML;
1856     },
1857     
1858     setRightTitle : function(v)
1859     {
1860         var t = this.el.select('.panel-header-right',true).first();
1861         
1862         if(!t){
1863             return;
1864         }
1865         
1866         t.dom.innerHTML = v;
1867     },
1868     
1869     onClick : function(e)
1870     {
1871         e.preventDefault();
1872         
1873         this.fireEvent('click', this, e);
1874     }
1875 });
1876
1877  /*
1878  *  - LGPL
1879  *
1880  *  This is BS4's Card element.. - similar to our containers probably..
1881  * 
1882  */
1883 /**
1884  * @class Roo.bootstrap.Card
1885  * @extends Roo.bootstrap.Component
1886  * Bootstrap Card class
1887  *
1888  *
1889  * possible... may not be implemented..
1890  * @cfg {String} header_image  src url of image.
1891  * @cfg {String|Object} header
1892  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1893  * 
1894  * @cfg {String} title
1895  * @cfg {String} subtitle
1896  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1897  * @cfg {String} footer
1898  
1899  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1900  * 
1901  * @cfg {String} margin (0|1|2|3|4|5|auto)
1902  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1903  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1904  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1905  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1906  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1907  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1908  *
1909  * @cfg {String} padding (0|1|2|3|4|5)
1910  * @cfg {String} padding_top (0|1|2|3|4|5)
1911  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1912  * @cfg {String} padding_left (0|1|2|3|4|5)
1913  * @cfg {String} padding_right (0|1|2|3|4|5)
1914  * @cfg {String} padding_x (0|1|2|3|4|5)
1915  * @cfg {String} padding_y (0|1|2|3|4|5)
1916  *
1917  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1918  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1919  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1920  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1921  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1922  
1923  * @config {Boolean} dragable  if this card can be dragged.
1924  * @config {String} drag_group  group for drag
1925  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1926  * @config {String} drop_group  group for drag
1927  * 
1928  * @config {Boolean} collapsable can the body be collapsed.
1929  * @config {Boolean} collapsed is the body collapsed when rendered...
1930  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1931  * @config {Boolean} rotated is the body rotated when rendered...
1932  * 
1933  * @constructor
1934  * Create a new Container
1935  * @param {Object} config The config object
1936  */
1937
1938 Roo.bootstrap.Card = function(config){
1939     Roo.bootstrap.Card.superclass.constructor.call(this, config);
1940     
1941     this.addEvents({
1942          // raw events
1943         /**
1944          * @event drop
1945          * When a element a card is dropped
1946          * @param {Roo.bootstrap.Element} this
1947          * @param {Roo.Element} n the node being dropped?
1948          * @param {Object} dd Drag and drop data
1949          * @param {Roo.EventObject} e
1950          * @param {Roo.EventObject} data  the data passed via getDragData
1951          */
1952         'drop' : true,
1953          /**
1954          * @event rotate
1955          * When a element a card is rotate
1956          * @param {Roo.bootstrap.Element} this
1957          * @param {Roo.Element} n the node being dropped?
1958          * @param {Boolean} rotate status
1959          */
1960         'rotate' : true
1961         
1962     });
1963 };
1964
1965
1966 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
1967     
1968     
1969     weight : '',
1970     
1971     margin: '', /// may be better in component?
1972     margin_top: '', 
1973     margin_bottom: '', 
1974     margin_left: '',
1975     margin_right: '',
1976     margin_x: '',
1977     margin_y: '',
1978     
1979     padding : '',
1980     padding_top: '', 
1981     padding_bottom: '', 
1982     padding_left: '',
1983     padding_right: '',
1984     padding_x: '',
1985     padding_y: '',
1986     
1987     display: '', 
1988     display_xs: '', 
1989     display_sm: '', 
1990     display_lg: '',
1991     display_xl: '',
1992  
1993     header_image  : '',
1994     header : '',
1995     header_size : 0,
1996     title : '',
1997     subtitle : '',
1998     html : '',
1999     footer: '',
2000
2001     collapsable : false,
2002     collapsed : false,
2003     rotateable : false,
2004     rotated : false,
2005     
2006     dragable : false,
2007     drag_group : false,
2008     dropable : false,
2009     drop_group : false,
2010     childContainer : false,
2011     dropEl : false, /// the dom placeholde element that indicates drop location.
2012     
2013     layoutCls : function()
2014     {
2015         var cls = '';
2016         var t = this;
2017         Roo.log(this.margin_bottom.length);
2018         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2019             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2020             
2021             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2022                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2023             }
2024             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2025                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2026             }
2027         });
2028         
2029         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2030             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2031                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
2032             }
2033         });
2034         
2035         // more generic support?
2036         if (this.hidden) {
2037             cls += ' d-none';
2038         }
2039         
2040         return cls;
2041     },
2042  
2043        // Roo.log("Call onRender: " + this.xtype);
2044         /*  We are looking at something like this.
2045 <div class="card">
2046     <img src="..." class="card-img-top" alt="...">
2047     <div class="card-body">
2048         <h5 class="card-title">Card title</h5>
2049          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2050
2051         >> this bit is really the body...
2052         <div> << we will ad dthis in hopefully it will not break shit.
2053         
2054         ** card text does not actually have any styling...
2055         
2056             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2057         
2058         </div> <<
2059           <a href="#" class="card-link">Card link</a>
2060           
2061     </div>
2062     <div class="card-footer">
2063         <small class="text-muted">Last updated 3 mins ago</small>
2064     </div>
2065 </div>
2066          */
2067     getAutoCreate : function(){
2068         
2069         var cfg = {
2070             tag : 'div',
2071             cls : 'card',
2072             cn : [ ]
2073         };
2074         
2075         if (this.weight.length && this.weight != 'light') {
2076             cfg.cls += ' text-white';
2077         } else {
2078             cfg.cls += ' text-dark'; // need as it's nested..
2079         }
2080         if (this.weight.length) {
2081             cfg.cls += ' bg-' + this.weight;
2082         }
2083         
2084         cfg.cls += this.layoutCls(); 
2085         
2086         var hdr = false;
2087         if (this.header.length) {
2088             hdr = {
2089                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2090                 cls : 'card-header',
2091                 cn : []
2092             };
2093             cfg.cn.push(hdr);
2094             hdr_ctr = hdr;
2095         } else {
2096             hdr = {
2097                 tag : 'div',
2098                 cls : 'card-header d-none',
2099                 cn : []
2100             };
2101             cfg.cn.push(hdr);
2102         }
2103         if (this.collapsable) {
2104             hdr_ctr = {
2105             tag : 'a',
2106             cls : 'd-block user-select-none',
2107             cn: [
2108                     {
2109                         tag: 'i',
2110                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2111                     }
2112                    
2113                 ]
2114             };
2115             hdr.cn.push(hdr_ctr);
2116         }
2117         
2118         hdr_ctr.cn.push(        {
2119             tag: 'span',
2120             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2121             html : this.header
2122         });
2123         
2124         
2125         if (this.header_image.length) {
2126             cfg.cn.push({
2127                 tag : 'img',
2128                 cls : 'card-img-top',
2129                 src: this.header_image // escape?
2130             });
2131         } else {
2132             cfg.cn.push({
2133                     tag : 'div',
2134                     cls : 'card-img-top d-none' 
2135                 });
2136         }
2137             
2138         var body = {
2139             tag : 'div',
2140             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2141             cn : []
2142         };
2143         var obody = body;
2144         if (this.collapsable || this.rotateable) {
2145             obody = {
2146                 tag: 'div',
2147                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2148                 cn : [  body ]
2149             };
2150         }
2151         
2152         cfg.cn.push(obody);
2153         
2154         if (this.title.length) {
2155             body.cn.push({
2156                 tag : 'div',
2157                 cls : 'card-title',
2158                 src: this.title // escape?
2159             });
2160         }  
2161         
2162         if (this.subtitle.length) {
2163             body.cn.push({
2164                 tag : 'div',
2165                 cls : 'card-title',
2166                 src: this.subtitle // escape?
2167             });
2168         }
2169         
2170         body.cn.push({
2171             tag : 'div',
2172             cls : 'roo-card-body-ctr'
2173         });
2174         
2175         if (this.html.length) {
2176             body.cn.push({
2177                 tag: 'div',
2178                 html : this.html
2179             });
2180         }
2181         // fixme ? handle objects?
2182         
2183         if (this.footer.length) {
2184            
2185             cfg.cn.push({
2186                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2187                 html : this.footer
2188             });
2189             
2190         } else {
2191             cfg.cn.push({cls : 'card-footer d-none'});
2192         }
2193         
2194         // footer...
2195         
2196         return cfg;
2197     },
2198     
2199     
2200     getCardHeader : function()
2201     {
2202         var  ret = this.el.select('.card-header',true).first();
2203         if (ret.hasClass('d-none')) {
2204             ret.removeClass('d-none');
2205         }
2206         
2207         return ret;
2208     },
2209     getCardFooter : function()
2210     {
2211         var  ret = this.el.select('.card-footer',true).first();
2212         if (ret.hasClass('d-none')) {
2213             ret.removeClass('d-none');
2214         }
2215         
2216         return ret;
2217     },
2218     getCardImageTop : function()
2219     {
2220         var  ret = this.el.select('.card-img-top',true).first();
2221         if (ret.hasClass('d-none')) {
2222             ret.removeClass('d-none');
2223         }
2224             
2225         return ret;
2226     },
2227     
2228     getChildContainer : function()
2229     {
2230         
2231         if(!this.el){
2232             return false;
2233         }
2234         return this.el.select('.roo-card-body-ctr',true).first();    
2235     },
2236     
2237     initEvents: function() 
2238     {
2239         
2240         this.bodyEl = this.getChildContainer();
2241         if(this.dragable){
2242             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2243                     containerScroll: true,
2244                     ddGroup: this.drag_group || 'default_card_drag_group'
2245             });
2246             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2247         }
2248         if (this.dropable) {
2249             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2250                 containerScroll: true,
2251                 ddGroup: this.drop_group || 'default_card_drag_group'
2252             });
2253             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2254             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2255             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2256             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2257             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2258         }
2259         
2260         if (this.collapsable) {
2261             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2262         }
2263         if (this.rotateable) {
2264             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2265         }
2266         this.collapsableEl = this.el.select('.roo-collapsable').first();
2267          
2268         this.footerEl = this.el.select('.card-footer').first();
2269         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2270         this.headerEl = this.el.select('.roo-card-header-ctr').first();
2271         
2272         if (this.rotated) {
2273             this.el.addClass('roo-card-rotated');
2274             this.fireEvent('rotate', this, true);
2275         }
2276         
2277     },
2278     getDragData : function(e)
2279     {
2280         var target = this.getEl();
2281         if (target) {
2282             //this.handleSelection(e);
2283             
2284             var dragData = {
2285                 source: this,
2286                 copy: false,
2287                 nodes: this.getEl(),
2288                 records: []
2289             };
2290             
2291             
2292             dragData.ddel = target.dom ;    // the div element
2293             Roo.log(target.getWidth( ));
2294             dragData.ddel.style.width = target.getWidth() + 'px';
2295             
2296             return dragData;
2297         }
2298         return false;
2299     },
2300     /**
2301     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2302     *    whole Element becomes the target, and this causes the drop gesture to append.
2303     */
2304     getTargetFromEvent : function(e, dragged_card_el)
2305     {
2306         var target = e.getTarget();
2307         while ((target !== null) && (target.parentNode != this.bodyEl.dom)) {
2308             target = target.parentNode;
2309         }
2310         
2311         var ret = {
2312             position: '',
2313             cards : [],
2314             card_n : -1,
2315             items_n : -1,
2316             card : false 
2317         };
2318         
2319         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2320         // see if target is one of the 'cards'...
2321         
2322         
2323         //Roo.log(this.items.length);
2324         var pos = false;
2325         
2326         var last_card_n = 0;
2327         var cards_len  = 0;
2328         for (var i = 0;i< this.items.length;i++) {
2329             
2330             if (!this.items[i].el.hasClass('card')) {
2331                  continue;
2332             }
2333             pos = this.getDropPoint(e, this.items[i].el.dom);
2334             
2335             cards_len = ret.cards.length;
2336             //Roo.log(this.items[i].el.dom.id);
2337             ret.cards.push(this.items[i]);
2338             last_card_n  = i;
2339             if (ret.card_n < 0 && pos == 'above') {
2340                 ret.position = cards_len > 0 ? 'below' : pos;
2341                 ret.items_n = i > 0 ? i - 1 : 0;
2342                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2343                 ret.card = ret.cards[ret.card_n];
2344             }
2345         }
2346         if (!ret.cards.length) {
2347             ret.card = true;
2348             ret.position = 'below';
2349             ret.items_n;
2350             return ret;
2351         }
2352         // could not find a card.. stick it at the end..
2353         if (ret.card_n < 0) {
2354             ret.card_n = last_card_n;
2355             ret.card = ret.cards[last_card_n];
2356             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2357             ret.position = 'below';
2358         }
2359         
2360         if (this.items[ret.items_n].el == dragged_card_el) {
2361             return false;
2362         }
2363         
2364         if (ret.position == 'below') {
2365             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2366             
2367             if (card_after  && card_after.el == dragged_card_el) {
2368                 return false;
2369             }
2370             return ret;
2371         }
2372         
2373         // its's after ..
2374         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2375         
2376         if (card_before  && card_before.el == dragged_card_el) {
2377             return false;
2378         }
2379         
2380         return ret;
2381     },
2382     
2383     onNodeEnter : function(n, dd, e, data){
2384         return false;
2385     },
2386     onNodeOver : function(n, dd, e, data)
2387     {
2388        
2389         var target_info = this.getTargetFromEvent(e,data.source.el);
2390         if (target_info === false) {
2391             this.dropPlaceHolder('hide');
2392             return false;
2393         }
2394         Roo.log(['getTargetFromEvent', target_info ]);
2395         
2396          
2397         this.dropPlaceHolder('show', target_info,data);
2398         
2399         return false; 
2400     },
2401     onNodeOut : function(n, dd, e, data){
2402         this.dropPlaceHolder('hide');
2403      
2404     },
2405     onNodeDrop : function(n, dd, e, data)
2406     {
2407         
2408         // call drop - return false if
2409         
2410         // this could actually fail - if the Network drops..
2411         // we will ignore this at present..- client should probably reload
2412         // the whole set of cards if stuff like that fails.
2413         
2414         
2415         var info = this.getTargetFromEvent(e,data.source.el);
2416         if (info === false) {
2417             return false;
2418         }
2419         
2420         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
2421             return false;
2422         }
2423          
2424         this.dropPlaceHolder('hide');
2425         
2426         // do the dom manipulation first..
2427         var dom = data.source.el.dom;
2428         dom.parentNode.removeChild(dom);
2429         
2430         
2431         if (info.card !== true) {
2432             var cardel = info.card.el.dom;
2433             
2434             if (info.position == 'above') {
2435                 cardel.parentNode.insertBefore(dom, cardel);
2436             } else if (cardel.nextSibling) {
2437                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2438             } else {
2439                 cardel.parentNode.append(dom);
2440             }
2441         } else {
2442             // card container???
2443             this.bodyEl.dom.append(dom);
2444         }
2445         
2446         //FIXME HANDLE card = true 
2447         
2448         // add this to the correct place in items.
2449         
2450         
2451         
2452         // remove Card from items.
2453         
2454         var old_parent = data.source.parent();
2455         
2456         old_parent.items = old_parent.items.filter(function(e) { return e != data.source });
2457         
2458         if (this.items.length) {
2459             var nitems = [];
2460             //Roo.log([info.items_n, info.position, this.items.length]);
2461             for (var i =0; i < this.items.length; i++) {
2462                 if (i == info.items_n && info.position == 'above') {
2463                     nitems.push(data.source);
2464                 }
2465                 nitems.push(this.items[i]);
2466                 if (i == info.items_n && info.position == 'below') {
2467                     nitems.push(data.source);
2468                 }
2469             }
2470             this.items = nitems;
2471             Roo.log(this.items);
2472         } else {
2473             this.items.push(data.source);
2474         }
2475         
2476         data.source.parentId = this.id;
2477         
2478         return true;
2479     },
2480     
2481     /**    Decide whether to drop above or below a View node. */
2482     getDropPoint : function(e, n, dd)
2483     {
2484         if (dd) {
2485              return false;
2486         }
2487         if (n == this.bodyEl.dom) {
2488             return "above";
2489         }
2490         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2491         var c = t + (b - t) / 2;
2492         var y = Roo.lib.Event.getPageY(e);
2493         if(y <= c) {
2494             return "above";
2495         }else{
2496             return "below";
2497         }
2498     },
2499     onToggleCollapse : function(e)
2500         {
2501         if (this.collapsed) {
2502             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2503             this.collapsableEl.addClass('show');
2504             this.collapsed = false;
2505             return;
2506         }
2507         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2508         this.collapsableEl.removeClass('show');
2509         this.collapsed = true;
2510         
2511     
2512     },
2513     
2514     onToggleRotate : function(e)
2515     {
2516         this.collapsableEl.removeClass('show');
2517         this.footerEl.removeClass('d-none');
2518         this.el.removeClass('roo-card-rotated');
2519         this.el.removeClass('d-none');
2520         if (this.rotated) {
2521             
2522             this.collapsableEl.addClass('show');
2523             this.rotated = false;
2524             this.fireEvent('rotate', this, this.rotated);
2525             return;
2526         }
2527         this.el.addClass('roo-card-rotated');
2528         this.footerEl.addClass('d-none');
2529         this.el.select('.roo-collapsable').removeClass('show');
2530         
2531         this.rotated = true;
2532         this.fireEvent('rotate', this, this.rotated);
2533     
2534     },
2535     
2536     dropPlaceHolder: function (action, info, data)
2537     {
2538         if (this.dropEl === false) {
2539             this.dropEl = Roo.DomHelper.append(this.bodyEl, {
2540             cls : 'd-none'
2541             },true);
2542         }
2543         this.dropEl.removeClass(['d-none', 'd-block']);        
2544         if (action == 'hide') {
2545             
2546             this.dropEl.addClass('d-none');
2547             return;
2548         }
2549         // FIXME - info.card == true!!!
2550         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2551         
2552         if (info.card !== true) {
2553             var cardel = info.card.el.dom;
2554             
2555             if (info.position == 'above') {
2556                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2557             } else if (cardel.nextSibling) {
2558                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2559             } else {
2560                 cardel.parentNode.append(this.dropEl.dom);
2561             }
2562         } else {
2563             // card container???
2564             this.bodyEl.dom.append(this.dropEl.dom);
2565         }
2566         
2567         this.dropEl.addClass('d-block roo-card-dropzone');
2568         
2569         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2570         
2571         
2572     
2573     
2574     
2575     },
2576     setHeaderText: function(html)
2577     {
2578         this.headerEl.dom.innerHTML = html;
2579     }
2580
2581     
2582 });
2583
2584 /*
2585  * - LGPL
2586  *
2587  * Card header - holder for the card header elements.
2588  * 
2589  */
2590
2591 /**
2592  * @class Roo.bootstrap.CardHeader
2593  * @extends Roo.bootstrap.Element
2594  * Bootstrap CardHeader class
2595  * @constructor
2596  * Create a new Card Header - that you can embed children into
2597  * @param {Object} config The config object
2598  */
2599
2600 Roo.bootstrap.CardHeader = function(config){
2601     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2602 };
2603
2604 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2605     
2606     
2607     container_method : 'getCardHeader' 
2608     
2609      
2610     
2611     
2612    
2613 });
2614
2615  
2616
2617  /*
2618  * - LGPL
2619  *
2620  * Card footer - holder for the card footer elements.
2621  * 
2622  */
2623
2624 /**
2625  * @class Roo.bootstrap.CardFooter
2626  * @extends Roo.bootstrap.Element
2627  * Bootstrap CardFooter class
2628  * @constructor
2629  * Create a new Card Footer - that you can embed children into
2630  * @param {Object} config The config object
2631  */
2632
2633 Roo.bootstrap.CardFooter = function(config){
2634     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2635 };
2636
2637 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2638     
2639     
2640     container_method : 'getCardFooter' 
2641     
2642      
2643     
2644     
2645    
2646 });
2647
2648  
2649
2650  /*
2651  * - LGPL
2652  *
2653  * Card header - holder for the card header elements.
2654  * 
2655  */
2656
2657 /**
2658  * @class Roo.bootstrap.CardImageTop
2659  * @extends Roo.bootstrap.Element
2660  * Bootstrap CardImageTop class
2661  * @constructor
2662  * Create a new Card Image Top container
2663  * @param {Object} config The config object
2664  */
2665
2666 Roo.bootstrap.CardImageTop = function(config){
2667     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2668 };
2669
2670 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2671     
2672    
2673     container_method : 'getCardImageTop' 
2674     
2675      
2676     
2677    
2678 });
2679
2680  
2681
2682  /*
2683  * - LGPL
2684  *
2685  * image
2686  * 
2687  */
2688
2689
2690 /**
2691  * @class Roo.bootstrap.Img
2692  * @extends Roo.bootstrap.Component
2693  * Bootstrap Img class
2694  * @cfg {Boolean} imgResponsive false | true
2695  * @cfg {String} border rounded | circle | thumbnail
2696  * @cfg {String} src image source
2697  * @cfg {String} alt image alternative text
2698  * @cfg {String} href a tag href
2699  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2700  * @cfg {String} xsUrl xs image source
2701  * @cfg {String} smUrl sm image source
2702  * @cfg {String} mdUrl md image source
2703  * @cfg {String} lgUrl lg image source
2704  * 
2705  * @constructor
2706  * Create a new Input
2707  * @param {Object} config The config object
2708  */
2709
2710 Roo.bootstrap.Img = function(config){
2711     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2712     
2713     this.addEvents({
2714         // img events
2715         /**
2716          * @event click
2717          * The img click event for the img.
2718          * @param {Roo.EventObject} e
2719          */
2720         "click" : true
2721     });
2722 };
2723
2724 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2725     
2726     imgResponsive: true,
2727     border: '',
2728     src: 'about:blank',
2729     href: false,
2730     target: false,
2731     xsUrl: '',
2732     smUrl: '',
2733     mdUrl: '',
2734     lgUrl: '',
2735
2736     getAutoCreate : function()
2737     {   
2738         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2739             return this.createSingleImg();
2740         }
2741         
2742         var cfg = {
2743             tag: 'div',
2744             cls: 'roo-image-responsive-group',
2745             cn: []
2746         };
2747         var _this = this;
2748         
2749         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2750             
2751             if(!_this[size + 'Url']){
2752                 return;
2753             }
2754             
2755             var img = {
2756                 tag: 'img',
2757                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2758                 html: _this.html || cfg.html,
2759                 src: _this[size + 'Url']
2760             };
2761             
2762             img.cls += ' roo-image-responsive-' + size;
2763             
2764             var s = ['xs', 'sm', 'md', 'lg'];
2765             
2766             s.splice(s.indexOf(size), 1);
2767             
2768             Roo.each(s, function(ss){
2769                 img.cls += ' hidden-' + ss;
2770             });
2771             
2772             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2773                 cfg.cls += ' img-' + _this.border;
2774             }
2775             
2776             if(_this.alt){
2777                 cfg.alt = _this.alt;
2778             }
2779             
2780             if(_this.href){
2781                 var a = {
2782                     tag: 'a',
2783                     href: _this.href,
2784                     cn: [
2785                         img
2786                     ]
2787                 };
2788
2789                 if(this.target){
2790                     a.target = _this.target;
2791                 }
2792             }
2793             
2794             cfg.cn.push((_this.href) ? a : img);
2795             
2796         });
2797         
2798         return cfg;
2799     },
2800     
2801     createSingleImg : function()
2802     {
2803         var cfg = {
2804             tag: 'img',
2805             cls: (this.imgResponsive) ? 'img-responsive' : '',
2806             html : null,
2807             src : 'about:blank'  // just incase src get's set to undefined?!?
2808         };
2809         
2810         cfg.html = this.html || cfg.html;
2811         
2812         cfg.src = this.src || cfg.src;
2813         
2814         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2815             cfg.cls += ' img-' + this.border;
2816         }
2817         
2818         if(this.alt){
2819             cfg.alt = this.alt;
2820         }
2821         
2822         if(this.href){
2823             var a = {
2824                 tag: 'a',
2825                 href: this.href,
2826                 cn: [
2827                     cfg
2828                 ]
2829             };
2830             
2831             if(this.target){
2832                 a.target = this.target;
2833             }
2834             
2835         }
2836         
2837         return (this.href) ? a : cfg;
2838     },
2839     
2840     initEvents: function() 
2841     {
2842         if(!this.href){
2843             this.el.on('click', this.onClick, this);
2844         }
2845         
2846     },
2847     
2848     onClick : function(e)
2849     {
2850         Roo.log('img onclick');
2851         this.fireEvent('click', this, e);
2852     },
2853     /**
2854      * Sets the url of the image - used to update it
2855      * @param {String} url the url of the image
2856      */
2857     
2858     setSrc : function(url)
2859     {
2860         this.src =  url;
2861         
2862         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2863             this.el.dom.src =  url;
2864             return;
2865         }
2866         
2867         this.el.select('img', true).first().dom.src =  url;
2868     }
2869     
2870     
2871    
2872 });
2873
2874  /*
2875  * - LGPL
2876  *
2877  * image
2878  * 
2879  */
2880
2881
2882 /**
2883  * @class Roo.bootstrap.Link
2884  * @extends Roo.bootstrap.Component
2885  * Bootstrap Link Class
2886  * @cfg {String} alt image alternative text
2887  * @cfg {String} href a tag href
2888  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2889  * @cfg {String} html the content of the link.
2890  * @cfg {String} anchor name for the anchor link
2891  * @cfg {String} fa - favicon
2892
2893  * @cfg {Boolean} preventDefault (true | false) default false
2894
2895  * 
2896  * @constructor
2897  * Create a new Input
2898  * @param {Object} config The config object
2899  */
2900
2901 Roo.bootstrap.Link = function(config){
2902     Roo.bootstrap.Link.superclass.constructor.call(this, config);
2903     
2904     this.addEvents({
2905         // img events
2906         /**
2907          * @event click
2908          * The img click event for the img.
2909          * @param {Roo.EventObject} e
2910          */
2911         "click" : true
2912     });
2913 };
2914
2915 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
2916     
2917     href: false,
2918     target: false,
2919     preventDefault: false,
2920     anchor : false,
2921     alt : false,
2922     fa: false,
2923
2924
2925     getAutoCreate : function()
2926     {
2927         var html = this.html || '';
2928         
2929         if (this.fa !== false) {
2930             html = '<i class="fa fa-' + this.fa + '"></i>';
2931         }
2932         var cfg = {
2933             tag: 'a'
2934         };
2935         // anchor's do not require html/href...
2936         if (this.anchor === false) {
2937             cfg.html = html;
2938             cfg.href = this.href || '#';
2939         } else {
2940             cfg.name = this.anchor;
2941             if (this.html !== false || this.fa !== false) {
2942                 cfg.html = html;
2943             }
2944             if (this.href !== false) {
2945                 cfg.href = this.href;
2946             }
2947         }
2948         
2949         if(this.alt !== false){
2950             cfg.alt = this.alt;
2951         }
2952         
2953         
2954         if(this.target !== false) {
2955             cfg.target = this.target;
2956         }
2957         
2958         return cfg;
2959     },
2960     
2961     initEvents: function() {
2962         
2963         if(!this.href || this.preventDefault){
2964             this.el.on('click', this.onClick, this);
2965         }
2966     },
2967     
2968     onClick : function(e)
2969     {
2970         if(this.preventDefault){
2971             e.preventDefault();
2972         }
2973         //Roo.log('img onclick');
2974         this.fireEvent('click', this, e);
2975     }
2976    
2977 });
2978
2979  /*
2980  * - LGPL
2981  *
2982  * header
2983  * 
2984  */
2985
2986 /**
2987  * @class Roo.bootstrap.Header
2988  * @extends Roo.bootstrap.Component
2989  * Bootstrap Header class
2990  * @cfg {String} html content of header
2991  * @cfg {Number} level (1|2|3|4|5|6) default 1
2992  * 
2993  * @constructor
2994  * Create a new Header
2995  * @param {Object} config The config object
2996  */
2997
2998
2999 Roo.bootstrap.Header  = function(config){
3000     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3001 };
3002
3003 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3004     
3005     //href : false,
3006     html : false,
3007     level : 1,
3008     
3009     
3010     
3011     getAutoCreate : function(){
3012         
3013         
3014         
3015         var cfg = {
3016             tag: 'h' + (1 *this.level),
3017             html: this.html || ''
3018         } ;
3019         
3020         return cfg;
3021     }
3022    
3023 });
3024
3025  
3026
3027  /*
3028  * Based on:
3029  * Ext JS Library 1.1.1
3030  * Copyright(c) 2006-2007, Ext JS, LLC.
3031  *
3032  * Originally Released Under LGPL - original licence link has changed is not relivant.
3033  *
3034  * Fork - LGPL
3035  * <script type="text/javascript">
3036  */
3037  
3038 /**
3039  * @class Roo.bootstrap.MenuMgr
3040  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3041  * @singleton
3042  */
3043 Roo.bootstrap.MenuMgr = function(){
3044    var menus, active, groups = {}, attached = false, lastShow = new Date();
3045
3046    // private - called when first menu is created
3047    function init(){
3048        menus = {};
3049        active = new Roo.util.MixedCollection();
3050        Roo.get(document).addKeyListener(27, function(){
3051            if(active.length > 0){
3052                hideAll();
3053            }
3054        });
3055    }
3056
3057    // private
3058    function hideAll(){
3059        if(active && active.length > 0){
3060            var c = active.clone();
3061            c.each(function(m){
3062                m.hide();
3063            });
3064        }
3065    }
3066
3067    // private
3068    function onHide(m){
3069        active.remove(m);
3070        if(active.length < 1){
3071            Roo.get(document).un("mouseup", onMouseDown);
3072             
3073            attached = false;
3074        }
3075    }
3076
3077    // private
3078    function onShow(m){
3079        var last = active.last();
3080        lastShow = new Date();
3081        active.add(m);
3082        if(!attached){
3083           Roo.get(document).on("mouseup", onMouseDown);
3084            
3085            attached = true;
3086        }
3087        if(m.parentMenu){
3088           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3089           m.parentMenu.activeChild = m;
3090        }else if(last && last.isVisible()){
3091           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3092        }
3093    }
3094
3095    // private
3096    function onBeforeHide(m){
3097        if(m.activeChild){
3098            m.activeChild.hide();
3099        }
3100        if(m.autoHideTimer){
3101            clearTimeout(m.autoHideTimer);
3102            delete m.autoHideTimer;
3103        }
3104    }
3105
3106    // private
3107    function onBeforeShow(m){
3108        var pm = m.parentMenu;
3109        if(!pm && !m.allowOtherMenus){
3110            hideAll();
3111        }else if(pm && pm.activeChild && active != m){
3112            pm.activeChild.hide();
3113        }
3114    }
3115
3116    // private this should really trigger on mouseup..
3117    function onMouseDown(e){
3118         Roo.log("on Mouse Up");
3119         
3120         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3121             Roo.log("MenuManager hideAll");
3122             hideAll();
3123             e.stopEvent();
3124         }
3125         
3126         
3127    }
3128
3129    // private
3130    function onBeforeCheck(mi, state){
3131        if(state){
3132            var g = groups[mi.group];
3133            for(var i = 0, l = g.length; i < l; i++){
3134                if(g[i] != mi){
3135                    g[i].setChecked(false);
3136                }
3137            }
3138        }
3139    }
3140
3141    return {
3142
3143        /**
3144         * Hides all menus that are currently visible
3145         */
3146        hideAll : function(){
3147             hideAll();  
3148        },
3149
3150        // private
3151        register : function(menu){
3152            if(!menus){
3153                init();
3154            }
3155            menus[menu.id] = menu;
3156            menu.on("beforehide", onBeforeHide);
3157            menu.on("hide", onHide);
3158            menu.on("beforeshow", onBeforeShow);
3159            menu.on("show", onShow);
3160            var g = menu.group;
3161            if(g && menu.events["checkchange"]){
3162                if(!groups[g]){
3163                    groups[g] = [];
3164                }
3165                groups[g].push(menu);
3166                menu.on("checkchange", onCheck);
3167            }
3168        },
3169
3170         /**
3171          * Returns a {@link Roo.menu.Menu} object
3172          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3173          * be used to generate and return a new Menu instance.
3174          */
3175        get : function(menu){
3176            if(typeof menu == "string"){ // menu id
3177                return menus[menu];
3178            }else if(menu.events){  // menu instance
3179                return menu;
3180            }
3181            /*else if(typeof menu.length == 'number'){ // array of menu items?
3182                return new Roo.bootstrap.Menu({items:menu});
3183            }else{ // otherwise, must be a config
3184                return new Roo.bootstrap.Menu(menu);
3185            }
3186            */
3187            return false;
3188        },
3189
3190        // private
3191        unregister : function(menu){
3192            delete menus[menu.id];
3193            menu.un("beforehide", onBeforeHide);
3194            menu.un("hide", onHide);
3195            menu.un("beforeshow", onBeforeShow);
3196            menu.un("show", onShow);
3197            var g = menu.group;
3198            if(g && menu.events["checkchange"]){
3199                groups[g].remove(menu);
3200                menu.un("checkchange", onCheck);
3201            }
3202        },
3203
3204        // private
3205        registerCheckable : function(menuItem){
3206            var g = menuItem.group;
3207            if(g){
3208                if(!groups[g]){
3209                    groups[g] = [];
3210                }
3211                groups[g].push(menuItem);
3212                menuItem.on("beforecheckchange", onBeforeCheck);
3213            }
3214        },
3215
3216        // private
3217        unregisterCheckable : function(menuItem){
3218            var g = menuItem.group;
3219            if(g){
3220                groups[g].remove(menuItem);
3221                menuItem.un("beforecheckchange", onBeforeCheck);
3222            }
3223        }
3224    };
3225 }();/*
3226  * - LGPL
3227  *
3228  * menu
3229  * 
3230  */
3231
3232 /**
3233  * @class Roo.bootstrap.Menu
3234  * @extends Roo.bootstrap.Component
3235  * Bootstrap Menu class - container for MenuItems
3236  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3237  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3238  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3239  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3240  * 
3241  * @constructor
3242  * Create a new Menu
3243  * @param {Object} config The config object
3244  */
3245
3246
3247 Roo.bootstrap.Menu = function(config){
3248     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3249     if (this.registerMenu && this.type != 'treeview')  {
3250         Roo.bootstrap.MenuMgr.register(this);
3251     }
3252     
3253     
3254     this.addEvents({
3255         /**
3256          * @event beforeshow
3257          * Fires before this menu is displayed (return false to block)
3258          * @param {Roo.menu.Menu} this
3259          */
3260         beforeshow : true,
3261         /**
3262          * @event beforehide
3263          * Fires before this menu is hidden (return false to block)
3264          * @param {Roo.menu.Menu} this
3265          */
3266         beforehide : true,
3267         /**
3268          * @event show
3269          * Fires after this menu is displayed
3270          * @param {Roo.menu.Menu} this
3271          */
3272         show : true,
3273         /**
3274          * @event hide
3275          * Fires after this menu is hidden
3276          * @param {Roo.menu.Menu} this
3277          */
3278         hide : true,
3279         /**
3280          * @event click
3281          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3282          * @param {Roo.menu.Menu} this
3283          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3284          * @param {Roo.EventObject} e
3285          */
3286         click : true,
3287         /**
3288          * @event mouseover
3289          * Fires when the mouse is hovering over this menu
3290          * @param {Roo.menu.Menu} this
3291          * @param {Roo.EventObject} e
3292          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3293          */
3294         mouseover : true,
3295         /**
3296          * @event mouseout
3297          * Fires when the mouse exits this menu
3298          * @param {Roo.menu.Menu} this
3299          * @param {Roo.EventObject} e
3300          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3301          */
3302         mouseout : true,
3303         /**
3304          * @event itemclick
3305          * Fires when a menu item contained in this menu is clicked
3306          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3307          * @param {Roo.EventObject} e
3308          */
3309         itemclick: true
3310     });
3311     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3312 };
3313
3314 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3315     
3316    /// html : false,
3317     //align : '',
3318     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3319     type: false,
3320     /**
3321      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3322      */
3323     registerMenu : true,
3324     
3325     menuItems :false, // stores the menu items..
3326     
3327     hidden:true,
3328         
3329     parentMenu : false,
3330     
3331     stopEvent : true,
3332     
3333     isLink : false,
3334     
3335     getChildContainer : function() {
3336         return this.el;  
3337     },
3338     
3339     getAutoCreate : function(){
3340          
3341         //if (['right'].indexOf(this.align)!==-1) {
3342         //    cfg.cn[1].cls += ' pull-right'
3343         //}
3344         
3345         
3346         var cfg = {
3347             tag : 'ul',
3348             cls : 'dropdown-menu' ,
3349             style : 'z-index:1000'
3350             
3351         };
3352         
3353         if (this.type === 'submenu') {
3354             cfg.cls = 'submenu active';
3355         }
3356         if (this.type === 'treeview') {
3357             cfg.cls = 'treeview-menu';
3358         }
3359         
3360         return cfg;
3361     },
3362     initEvents : function() {
3363         
3364        // Roo.log("ADD event");
3365        // Roo.log(this.triggerEl.dom);
3366         
3367         this.triggerEl.on('click', this.onTriggerClick, this);
3368         
3369         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3370         
3371         
3372         if (this.triggerEl.hasClass('nav-item')) {
3373             // dropdown toggle on the 'a' in BS4?
3374             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3375         } else {
3376             this.triggerEl.addClass('dropdown-toggle');
3377         }
3378         if (Roo.isTouch) {
3379             this.el.on('touchstart'  , this.onTouch, this);
3380         }
3381         this.el.on('click' , this.onClick, this);
3382
3383         this.el.on("mouseover", this.onMouseOver, this);
3384         this.el.on("mouseout", this.onMouseOut, this);
3385         
3386     },
3387     
3388     findTargetItem : function(e)
3389     {
3390         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3391         if(!t){
3392             return false;
3393         }
3394         //Roo.log(t);         Roo.log(t.id);
3395         if(t && t.id){
3396             //Roo.log(this.menuitems);
3397             return this.menuitems.get(t.id);
3398             
3399             //return this.items.get(t.menuItemId);
3400         }
3401         
3402         return false;
3403     },
3404     
3405     onTouch : function(e) 
3406     {
3407         Roo.log("menu.onTouch");
3408         //e.stopEvent(); this make the user popdown broken
3409         this.onClick(e);
3410     },
3411     
3412     onClick : function(e)
3413     {
3414         Roo.log("menu.onClick");
3415         
3416         var t = this.findTargetItem(e);
3417         if(!t || t.isContainer){
3418             return;
3419         }
3420         Roo.log(e);
3421         /*
3422         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3423             if(t == this.activeItem && t.shouldDeactivate(e)){
3424                 this.activeItem.deactivate();
3425                 delete this.activeItem;
3426                 return;
3427             }
3428             if(t.canActivate){
3429                 this.setActiveItem(t, true);
3430             }
3431             return;
3432             
3433             
3434         }
3435         */
3436        
3437         Roo.log('pass click event');
3438         
3439         t.onClick(e);
3440         
3441         this.fireEvent("click", this, t, e);
3442         
3443         var _this = this;
3444         
3445         if(!t.href.length || t.href == '#'){
3446             (function() { _this.hide(); }).defer(100);
3447         }
3448         
3449     },
3450     
3451     onMouseOver : function(e){
3452         var t  = this.findTargetItem(e);
3453         //Roo.log(t);
3454         //if(t){
3455         //    if(t.canActivate && !t.disabled){
3456         //        this.setActiveItem(t, true);
3457         //    }
3458         //}
3459         
3460         this.fireEvent("mouseover", this, e, t);
3461     },
3462     isVisible : function(){
3463         return !this.hidden;
3464     },
3465     onMouseOut : function(e){
3466         var t  = this.findTargetItem(e);
3467         
3468         //if(t ){
3469         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3470         //        this.activeItem.deactivate();
3471         //        delete this.activeItem;
3472         //    }
3473         //}
3474         this.fireEvent("mouseout", this, e, t);
3475     },
3476     
3477     
3478     /**
3479      * Displays this menu relative to another element
3480      * @param {String/HTMLElement/Roo.Element} element The element to align to
3481      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3482      * the element (defaults to this.defaultAlign)
3483      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3484      */
3485     show : function(el, pos, parentMenu)
3486     {
3487         if (false === this.fireEvent("beforeshow", this)) {
3488             Roo.log("show canceled");
3489             return;
3490         }
3491         this.parentMenu = parentMenu;
3492         if(!this.el){
3493             this.render();
3494         }
3495         
3496         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3497     },
3498      /**
3499      * Displays this menu at a specific xy position
3500      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3501      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3502      */
3503     showAt : function(xy, parentMenu, /* private: */_e){
3504         this.parentMenu = parentMenu;
3505         if(!this.el){
3506             this.render();
3507         }
3508         if(_e !== false){
3509             this.fireEvent("beforeshow", this);
3510             //xy = this.el.adjustForConstraints(xy);
3511         }
3512         
3513         //this.el.show();
3514         this.hideMenuItems();
3515         this.hidden = false;
3516         this.triggerEl.addClass('open');
3517         this.el.addClass('show');
3518         
3519         // reassign x when hitting right
3520         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3521             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3522         }
3523         
3524         // reassign y when hitting bottom
3525         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3526             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3527         }
3528         
3529         // but the list may align on trigger left or trigger top... should it be a properity?
3530         
3531         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3532             this.el.setXY(xy);
3533         }
3534         
3535         this.focus();
3536         this.fireEvent("show", this);
3537     },
3538     
3539     focus : function(){
3540         return;
3541         if(!this.hidden){
3542             this.doFocus.defer(50, this);
3543         }
3544     },
3545
3546     doFocus : function(){
3547         if(!this.hidden){
3548             this.focusEl.focus();
3549         }
3550     },
3551
3552     /**
3553      * Hides this menu and optionally all parent menus
3554      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3555      */
3556     hide : function(deep)
3557     {
3558         if (false === this.fireEvent("beforehide", this)) {
3559             Roo.log("hide canceled");
3560             return;
3561         }
3562         this.hideMenuItems();
3563         if(this.el && this.isVisible()){
3564            
3565             if(this.activeItem){
3566                 this.activeItem.deactivate();
3567                 this.activeItem = null;
3568             }
3569             this.triggerEl.removeClass('open');;
3570             this.el.removeClass('show');
3571             this.hidden = true;
3572             this.fireEvent("hide", this);
3573         }
3574         if(deep === true && this.parentMenu){
3575             this.parentMenu.hide(true);
3576         }
3577     },
3578     
3579     onTriggerClick : function(e)
3580     {
3581         Roo.log('trigger click');
3582         
3583         var target = e.getTarget();
3584         
3585         Roo.log(target.nodeName.toLowerCase());
3586         
3587         if(target.nodeName.toLowerCase() === 'i'){
3588             e.preventDefault();
3589         }
3590         
3591     },
3592     
3593     onTriggerPress  : function(e)
3594     {
3595         Roo.log('trigger press');
3596         //Roo.log(e.getTarget());
3597        // Roo.log(this.triggerEl.dom);
3598        
3599         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3600         var pel = Roo.get(e.getTarget());
3601         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3602             Roo.log('is treeview or dropdown?');
3603             return;
3604         }
3605         
3606         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3607             return;
3608         }
3609         
3610         if (this.isVisible()) {
3611             Roo.log('hide');
3612             this.hide();
3613         } else {
3614             Roo.log('show');
3615             this.show(this.triggerEl, '?', false);
3616         }
3617         
3618         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3619             e.stopEvent();
3620         }
3621         
3622     },
3623        
3624     
3625     hideMenuItems : function()
3626     {
3627         Roo.log("hide Menu Items");
3628         if (!this.el) { 
3629             return;
3630         }
3631         
3632         this.el.select('.open',true).each(function(aa) {
3633             
3634             aa.removeClass('open');
3635          
3636         });
3637     },
3638     addxtypeChild : function (tree, cntr) {
3639         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3640           
3641         this.menuitems.add(comp);
3642         return comp;
3643
3644     },
3645     getEl : function()
3646     {
3647         Roo.log(this.el);
3648         return this.el;
3649     },
3650     
3651     clear : function()
3652     {
3653         this.getEl().dom.innerHTML = '';
3654         this.menuitems.clear();
3655     }
3656 });
3657
3658  
3659  /*
3660  * - LGPL
3661  *
3662  * menu item
3663  * 
3664  */
3665
3666
3667 /**
3668  * @class Roo.bootstrap.MenuItem
3669  * @extends Roo.bootstrap.Component
3670  * Bootstrap MenuItem class
3671  * @cfg {String} html the menu label
3672  * @cfg {String} href the link
3673  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3674  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3675  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3676  * @cfg {String} fa favicon to show on left of menu item.
3677  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3678  * 
3679  * 
3680  * @constructor
3681  * Create a new MenuItem
3682  * @param {Object} config The config object
3683  */
3684
3685
3686 Roo.bootstrap.MenuItem = function(config){
3687     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3688     this.addEvents({
3689         // raw events
3690         /**
3691          * @event click
3692          * The raw click event for the entire grid.
3693          * @param {Roo.bootstrap.MenuItem} this
3694          * @param {Roo.EventObject} e
3695          */
3696         "click" : true
3697     });
3698 };
3699
3700 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3701     
3702     href : false,
3703     html : false,
3704     preventDefault: false,
3705     isContainer : false,
3706     active : false,
3707     fa: false,
3708     
3709     getAutoCreate : function(){
3710         
3711         if(this.isContainer){
3712             return {
3713                 tag: 'li',
3714                 cls: 'dropdown-menu-item '
3715             };
3716         }
3717         var ctag = {
3718             tag: 'span',
3719             html: 'Link'
3720         };
3721         
3722         var anc = {
3723             tag : 'a',
3724             cls : 'dropdown-item',
3725             href : '#',
3726             cn : [  ]
3727         };
3728         
3729         if (this.fa !== false) {
3730             anc.cn.push({
3731                 tag : 'i',
3732                 cls : 'fa fa-' + this.fa
3733             });
3734         }
3735         
3736         anc.cn.push(ctag);
3737         
3738         
3739         var cfg= {
3740             tag: 'li',
3741             cls: 'dropdown-menu-item',
3742             cn: [ anc ]
3743         };
3744         if (this.parent().type == 'treeview') {
3745             cfg.cls = 'treeview-menu';
3746         }
3747         if (this.active) {
3748             cfg.cls += ' active';
3749         }
3750         
3751         
3752         
3753         anc.href = this.href || cfg.cn[0].href ;
3754         ctag.html = this.html || cfg.cn[0].html ;
3755         return cfg;
3756     },
3757     
3758     initEvents: function()
3759     {
3760         if (this.parent().type == 'treeview') {
3761             this.el.select('a').on('click', this.onClick, this);
3762         }
3763         
3764         if (this.menu) {
3765             this.menu.parentType = this.xtype;
3766             this.menu.triggerEl = this.el;
3767             this.menu = this.addxtype(Roo.apply({}, this.menu));
3768         }
3769         
3770     },
3771     onClick : function(e)
3772     {
3773         Roo.log('item on click ');
3774         
3775         if(this.preventDefault){
3776             e.preventDefault();
3777         }
3778         //this.parent().hideMenuItems();
3779         
3780         this.fireEvent('click', this, e);
3781     },
3782     getEl : function()
3783     {
3784         return this.el;
3785     } 
3786 });
3787
3788  
3789
3790  /*
3791  * - LGPL
3792  *
3793  * menu separator
3794  * 
3795  */
3796
3797
3798 /**
3799  * @class Roo.bootstrap.MenuSeparator
3800  * @extends Roo.bootstrap.Component
3801  * Bootstrap MenuSeparator class
3802  * 
3803  * @constructor
3804  * Create a new MenuItem
3805  * @param {Object} config The config object
3806  */
3807
3808
3809 Roo.bootstrap.MenuSeparator = function(config){
3810     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3811 };
3812
3813 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3814     
3815     getAutoCreate : function(){
3816         var cfg = {
3817             cls: 'divider',
3818             tag : 'li'
3819         };
3820         
3821         return cfg;
3822     }
3823    
3824 });
3825
3826  
3827
3828  
3829 /*
3830 * Licence: LGPL
3831 */
3832
3833 /**
3834  * @class Roo.bootstrap.Modal
3835  * @extends Roo.bootstrap.Component
3836  * Bootstrap Modal class
3837  * @cfg {String} title Title of dialog
3838  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3839  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3840  * @cfg {Boolean} specificTitle default false
3841  * @cfg {Array} buttons Array of buttons or standard button set..
3842  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3843  * @cfg {Boolean} animate default true
3844  * @cfg {Boolean} allow_close default true
3845  * @cfg {Boolean} fitwindow default false
3846  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3847  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3848  * @cfg {String} size (sm|lg) default empty
3849  * @cfg {Number} max_width set the max width of modal
3850  * @cfg {Boolean} editableTitle can the title be edited
3851
3852  *
3853  *
3854  * @constructor
3855  * Create a new Modal Dialog
3856  * @param {Object} config The config object
3857  */
3858
3859 Roo.bootstrap.Modal = function(config){
3860     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3861     this.addEvents({
3862         // raw events
3863         /**
3864          * @event btnclick
3865          * The raw btnclick event for the button
3866          * @param {Roo.EventObject} e
3867          */
3868         "btnclick" : true,
3869         /**
3870          * @event resize
3871          * Fire when dialog resize
3872          * @param {Roo.bootstrap.Modal} this
3873          * @param {Roo.EventObject} e
3874          */
3875         "resize" : true,
3876         /**
3877          * @event titlechanged
3878          * Fire when the editable title has been changed
3879          * @param {Roo.bootstrap.Modal} this
3880          * @param {Roo.EventObject} value
3881          */
3882         "titlechanged" : true 
3883         
3884     });
3885     this.buttons = this.buttons || [];
3886
3887     if (this.tmpl) {
3888         this.tmpl = Roo.factory(this.tmpl);
3889     }
3890
3891 };
3892
3893 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3894
3895     title : 'test dialog',
3896
3897     buttons : false,
3898
3899     // set on load...
3900
3901     html: false,
3902
3903     tmp: false,
3904
3905     specificTitle: false,
3906
3907     buttonPosition: 'right',
3908
3909     allow_close : true,
3910
3911     animate : true,
3912
3913     fitwindow: false,
3914     
3915      // private
3916     dialogEl: false,
3917     bodyEl:  false,
3918     footerEl:  false,
3919     titleEl:  false,
3920     closeEl:  false,
3921
3922     size: '',
3923     
3924     max_width: 0,
3925     
3926     max_height: 0,
3927     
3928     fit_content: false,
3929     editableTitle  : false,
3930
3931     onRender : function(ct, position)
3932     {
3933         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
3934
3935         if(!this.el){
3936             var cfg = Roo.apply({},  this.getAutoCreate());
3937             cfg.id = Roo.id();
3938             //if(!cfg.name){
3939             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
3940             //}
3941             //if (!cfg.name.length) {
3942             //    delete cfg.name;
3943            // }
3944             if (this.cls) {
3945                 cfg.cls += ' ' + this.cls;
3946             }
3947             if (this.style) {
3948                 cfg.style = this.style;
3949             }
3950             this.el = Roo.get(document.body).createChild(cfg, position);
3951         }
3952         //var type = this.el.dom.type;
3953
3954
3955         if(this.tabIndex !== undefined){
3956             this.el.dom.setAttribute('tabIndex', this.tabIndex);
3957         }
3958
3959         this.dialogEl = this.el.select('.modal-dialog',true).first();
3960         this.bodyEl = this.el.select('.modal-body',true).first();
3961         this.closeEl = this.el.select('.modal-header .close', true).first();
3962         this.headerEl = this.el.select('.modal-header',true).first();
3963         this.titleEl = this.el.select('.modal-title',true).first();
3964         this.footerEl = this.el.select('.modal-footer',true).first();
3965
3966         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
3967         
3968         //this.el.addClass("x-dlg-modal");
3969
3970         if (this.buttons.length) {
3971             Roo.each(this.buttons, function(bb) {
3972                 var b = Roo.apply({}, bb);
3973                 b.xns = b.xns || Roo.bootstrap;
3974                 b.xtype = b.xtype || 'Button';
3975                 if (typeof(b.listeners) == 'undefined') {
3976                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
3977                 }
3978
3979                 var btn = Roo.factory(b);
3980
3981                 btn.render(this.getButtonContainer());
3982
3983             },this);
3984         }
3985         // render the children.
3986         var nitems = [];
3987
3988         if(typeof(this.items) != 'undefined'){
3989             var items = this.items;
3990             delete this.items;
3991
3992             for(var i =0;i < items.length;i++) {
3993                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
3994             }
3995         }
3996
3997         this.items = nitems;
3998
3999         // where are these used - they used to be body/close/footer
4000
4001
4002         this.initEvents();
4003         //this.el.addClass([this.fieldClass, this.cls]);
4004
4005     },
4006
4007     getAutoCreate : function()
4008     {
4009         // we will default to modal-body-overflow - might need to remove or make optional later.
4010         var bdy = {
4011                 cls : 'modal-body enable-modal-body-overflow ', 
4012                 html : this.html || ''
4013         };
4014
4015         var title = {
4016             tag: 'h4',
4017             cls : 'modal-title',
4018             html : this.title
4019         };
4020
4021         if(this.specificTitle){ // WTF is this?
4022             title = this.title;
4023         }
4024
4025         var header = [];
4026         if (this.allow_close && Roo.bootstrap.version == 3) {
4027             header.push({
4028                 tag: 'button',
4029                 cls : 'close',
4030                 html : '&times'
4031             });
4032         }
4033
4034         header.push(title);
4035
4036         if (this.editableTitle) {
4037             header.push({
4038                 cls: 'form-control roo-editable-title d-none',
4039                 tag: 'input',
4040                 type: 'text'
4041             });
4042         }
4043         
4044         if (this.allow_close && Roo.bootstrap.version == 4) {
4045             header.push({
4046                 tag: 'button',
4047                 cls : 'close',
4048                 html : '&times'
4049             });
4050         }
4051         
4052         var size = '';
4053
4054         if(this.size.length){
4055             size = 'modal-' + this.size;
4056         }
4057         
4058         var footer = Roo.bootstrap.version == 3 ?
4059             {
4060                 cls : 'modal-footer',
4061                 cn : [
4062                     {
4063                         tag: 'div',
4064                         cls: 'btn-' + this.buttonPosition
4065                     }
4066                 ]
4067
4068             } :
4069             {  // BS4 uses mr-auto on left buttons....
4070                 cls : 'modal-footer'
4071             };
4072
4073             
4074
4075         
4076         
4077         var modal = {
4078             cls: "modal",
4079              cn : [
4080                 {
4081                     cls: "modal-dialog " + size,
4082                     cn : [
4083                         {
4084                             cls : "modal-content",
4085                             cn : [
4086                                 {
4087                                     cls : 'modal-header',
4088                                     cn : header
4089                                 },
4090                                 bdy,
4091                                 footer
4092                             ]
4093
4094                         }
4095                     ]
4096
4097                 }
4098             ]
4099         };
4100
4101         if(this.animate){
4102             modal.cls += ' fade';
4103         }
4104
4105         return modal;
4106
4107     },
4108     getChildContainer : function() {
4109
4110          return this.bodyEl;
4111
4112     },
4113     getButtonContainer : function() {
4114         
4115          return Roo.bootstrap.version == 4 ?
4116             this.el.select('.modal-footer',true).first()
4117             : this.el.select('.modal-footer div',true).first();
4118
4119     },
4120     initEvents : function()
4121     {
4122         if (this.allow_close) {
4123             this.closeEl.on('click', this.hide, this);
4124         }
4125         Roo.EventManager.onWindowResize(this.resize, this, true);
4126         if (this.editableTitle) {
4127             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4128             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4129             this.headerEditEl.on('keyup', function(e) {
4130                     if(e.isNavKeyPress()){
4131                             this.toggleHeaderInput(false)
4132                     }
4133                 }, this);
4134             this.headerEditEl.on('blur', function(e) {
4135                 this.toggleHeaderInput(false)
4136             },this);
4137         }
4138
4139     },
4140   
4141
4142     resize : function()
4143     {
4144         this.maskEl.setSize(
4145             Roo.lib.Dom.getViewWidth(true),
4146             Roo.lib.Dom.getViewHeight(true)
4147         );
4148         
4149         if (this.fitwindow) {
4150             
4151            
4152             this.setSize(
4153                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4154                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4155             );
4156             return;
4157         }
4158         
4159         if(this.max_width !== 0) {
4160             
4161             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4162             
4163             if(this.height) {
4164                 this.setSize(w, this.height);
4165                 return;
4166             }
4167             
4168             if(this.max_height) {
4169                 this.setSize(w,Math.min(
4170                     this.max_height,
4171                     Roo.lib.Dom.getViewportHeight(true) - 60
4172                 ));
4173                 
4174                 return;
4175             }
4176             
4177             if(!this.fit_content) {
4178                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4179                 return;
4180             }
4181             
4182             this.setSize(w, Math.min(
4183                 60 +
4184                 this.headerEl.getHeight() + 
4185                 this.footerEl.getHeight() + 
4186                 this.getChildHeight(this.bodyEl.dom.childNodes),
4187                 Roo.lib.Dom.getViewportHeight(true) - 60)
4188             );
4189         }
4190         
4191     },
4192
4193     setSize : function(w,h)
4194     {
4195         if (!w && !h) {
4196             return;
4197         }
4198         
4199         this.resizeTo(w,h);
4200     },
4201
4202     show : function() {
4203
4204         if (!this.rendered) {
4205             this.render();
4206         }
4207
4208         //this.el.setStyle('display', 'block');
4209         this.el.removeClass('hideing');
4210         this.el.dom.style.display='block';
4211         
4212         Roo.get(document.body).addClass('modal-open');
4213  
4214         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4215             
4216             (function(){
4217                 this.el.addClass('show');
4218                 this.el.addClass('in');
4219             }).defer(50, this);
4220         }else{
4221             this.el.addClass('show');
4222             this.el.addClass('in');
4223         }
4224
4225         // not sure how we can show data in here..
4226         //if (this.tmpl) {
4227         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4228         //}
4229
4230         Roo.get(document.body).addClass("x-body-masked");
4231         
4232         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4233         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4234         this.maskEl.dom.style.display = 'block';
4235         this.maskEl.addClass('show');
4236         
4237         
4238         this.resize();
4239         
4240         this.fireEvent('show', this);
4241
4242         // set zindex here - otherwise it appears to be ignored...
4243         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4244
4245         (function () {
4246             this.items.forEach( function(e) {
4247                 e.layout ? e.layout() : false;
4248
4249             });
4250         }).defer(100,this);
4251
4252     },
4253     hide : function()
4254     {
4255         if(this.fireEvent("beforehide", this) !== false){
4256             
4257             this.maskEl.removeClass('show');
4258             
4259             this.maskEl.dom.style.display = '';
4260             Roo.get(document.body).removeClass("x-body-masked");
4261             this.el.removeClass('in');
4262             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4263
4264             if(this.animate){ // why
4265                 this.el.addClass('hideing');
4266                 this.el.removeClass('show');
4267                 (function(){
4268                     if (!this.el.hasClass('hideing')) {
4269                         return; // it's been shown again...
4270                     }
4271                     
4272                     this.el.dom.style.display='';
4273
4274                     Roo.get(document.body).removeClass('modal-open');
4275                     this.el.removeClass('hideing');
4276                 }).defer(150,this);
4277                 
4278             }else{
4279                 this.el.removeClass('show');
4280                 this.el.dom.style.display='';
4281                 Roo.get(document.body).removeClass('modal-open');
4282
4283             }
4284             this.fireEvent('hide', this);
4285         }
4286     },
4287     isVisible : function()
4288     {
4289         
4290         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4291         
4292     },
4293
4294     addButton : function(str, cb)
4295     {
4296
4297
4298         var b = Roo.apply({}, { html : str } );
4299         b.xns = b.xns || Roo.bootstrap;
4300         b.xtype = b.xtype || 'Button';
4301         if (typeof(b.listeners) == 'undefined') {
4302             b.listeners = { click : cb.createDelegate(this)  };
4303         }
4304
4305         var btn = Roo.factory(b);
4306
4307         btn.render(this.getButtonContainer());
4308
4309         return btn;
4310
4311     },
4312
4313     setDefaultButton : function(btn)
4314     {
4315         //this.el.select('.modal-footer').()
4316     },
4317
4318     resizeTo: function(w,h)
4319     {
4320         this.dialogEl.setWidth(w);
4321         
4322         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4323
4324         this.bodyEl.setHeight(h - diff);
4325         
4326         this.fireEvent('resize', this);
4327     },
4328     
4329     setContentSize  : function(w, h)
4330     {
4331
4332     },
4333     onButtonClick: function(btn,e)
4334     {
4335         //Roo.log([a,b,c]);
4336         this.fireEvent('btnclick', btn.name, e);
4337     },
4338      /**
4339      * Set the title of the Dialog
4340      * @param {String} str new Title
4341      */
4342     setTitle: function(str) {
4343         this.titleEl.dom.innerHTML = str;
4344         this.title = str;
4345     },
4346     /**
4347      * Set the body of the Dialog
4348      * @param {String} str new Title
4349      */
4350     setBody: function(str) {
4351         this.bodyEl.dom.innerHTML = str;
4352     },
4353     /**
4354      * Set the body of the Dialog using the template
4355      * @param {Obj} data - apply this data to the template and replace the body contents.
4356      */
4357     applyBody: function(obj)
4358     {
4359         if (!this.tmpl) {
4360             Roo.log("Error - using apply Body without a template");
4361             //code
4362         }
4363         this.tmpl.overwrite(this.bodyEl, obj);
4364     },
4365     
4366     getChildHeight : function(child_nodes)
4367     {
4368         if(
4369             !child_nodes ||
4370             child_nodes.length == 0
4371         ) {
4372             return 0;
4373         }
4374         
4375         var child_height = 0;
4376         
4377         for(var i = 0; i < child_nodes.length; i++) {
4378             
4379             /*
4380             * for modal with tabs...
4381             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4382                 
4383                 var layout_childs = child_nodes[i].childNodes;
4384                 
4385                 for(var j = 0; j < layout_childs.length; j++) {
4386                     
4387                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4388                         
4389                         var layout_body_childs = layout_childs[j].childNodes;
4390                         
4391                         for(var k = 0; k < layout_body_childs.length; k++) {
4392                             
4393                             if(layout_body_childs[k].classList.contains('navbar')) {
4394                                 child_height += layout_body_childs[k].offsetHeight;
4395                                 continue;
4396                             }
4397                             
4398                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4399                                 
4400                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4401                                 
4402                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4403                                     
4404                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4405                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4406                                         continue;
4407                                     }
4408                                     
4409                                 }
4410                                 
4411                             }
4412                             
4413                         }
4414                     }
4415                 }
4416                 continue;
4417             }
4418             */
4419             
4420             child_height += child_nodes[i].offsetHeight;
4421             // Roo.log(child_nodes[i].offsetHeight);
4422         }
4423         
4424         return child_height;
4425     },
4426     toggleHeaderInput : function(is_edit)
4427     {
4428         
4429         if (is_edit && this.is_header_editing) {
4430             return; // already editing..
4431         }
4432         if (is_edit) {
4433     
4434             this.headerEditEl.dom.value = this.title;
4435             this.headerEditEl.removeClass('d-none');
4436             this.headerEditEl.dom.focus();
4437             this.titleEl.addClass('d-none');
4438             
4439             this.is_header_editing = true;
4440             return
4441         }
4442         // flip back to not editing.
4443         this.title = this.headerEditEl.dom.value;
4444         this.headerEditEl.addClass('d-none');
4445         this.titleEl.removeClass('d-none');
4446         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4447         this.is_header_editing = false;
4448         this.fireEvent('titlechanged', this, this.title);
4449     
4450             
4451         
4452     }
4453
4454 });
4455
4456
4457 Roo.apply(Roo.bootstrap.Modal,  {
4458     /**
4459          * Button config that displays a single OK button
4460          * @type Object
4461          */
4462         OK :  [{
4463             name : 'ok',
4464             weight : 'primary',
4465             html : 'OK'
4466         }],
4467         /**
4468          * Button config that displays Yes and No buttons
4469          * @type Object
4470          */
4471         YESNO : [
4472             {
4473                 name  : 'no',
4474                 html : 'No'
4475             },
4476             {
4477                 name  :'yes',
4478                 weight : 'primary',
4479                 html : 'Yes'
4480             }
4481         ],
4482
4483         /**
4484          * Button config that displays OK and Cancel buttons
4485          * @type Object
4486          */
4487         OKCANCEL : [
4488             {
4489                name : 'cancel',
4490                 html : 'Cancel'
4491             },
4492             {
4493                 name : 'ok',
4494                 weight : 'primary',
4495                 html : 'OK'
4496             }
4497         ],
4498         /**
4499          * Button config that displays Yes, No and Cancel buttons
4500          * @type Object
4501          */
4502         YESNOCANCEL : [
4503             {
4504                 name : 'yes',
4505                 weight : 'primary',
4506                 html : 'Yes'
4507             },
4508             {
4509                 name : 'no',
4510                 html : 'No'
4511             },
4512             {
4513                 name : 'cancel',
4514                 html : 'Cancel'
4515             }
4516         ],
4517         
4518         zIndex : 10001
4519 });
4520
4521 /*
4522  * - LGPL
4523  *
4524  * messagebox - can be used as a replace
4525  * 
4526  */
4527 /**
4528  * @class Roo.MessageBox
4529  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4530  * Example usage:
4531  *<pre><code>
4532 // Basic alert:
4533 Roo.Msg.alert('Status', 'Changes saved successfully.');
4534
4535 // Prompt for user data:
4536 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4537     if (btn == 'ok'){
4538         // process text value...
4539     }
4540 });
4541
4542 // Show a dialog using config options:
4543 Roo.Msg.show({
4544    title:'Save Changes?',
4545    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4546    buttons: Roo.Msg.YESNOCANCEL,
4547    fn: processResult,
4548    animEl: 'elId'
4549 });
4550 </code></pre>
4551  * @singleton
4552  */
4553 Roo.bootstrap.MessageBox = function(){
4554     var dlg, opt, mask, waitTimer;
4555     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4556     var buttons, activeTextEl, bwidth;
4557
4558     
4559     // private
4560     var handleButton = function(button){
4561         dlg.hide();
4562         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4563     };
4564
4565     // private
4566     var handleHide = function(){
4567         if(opt && opt.cls){
4568             dlg.el.removeClass(opt.cls);
4569         }
4570         //if(waitTimer){
4571         //    Roo.TaskMgr.stop(waitTimer);
4572         //    waitTimer = null;
4573         //}
4574     };
4575
4576     // private
4577     var updateButtons = function(b){
4578         var width = 0;
4579         if(!b){
4580             buttons["ok"].hide();
4581             buttons["cancel"].hide();
4582             buttons["yes"].hide();
4583             buttons["no"].hide();
4584             dlg.footerEl.hide();
4585             
4586             return width;
4587         }
4588         dlg.footerEl.show();
4589         for(var k in buttons){
4590             if(typeof buttons[k] != "function"){
4591                 if(b[k]){
4592                     buttons[k].show();
4593                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4594                     width += buttons[k].el.getWidth()+15;
4595                 }else{
4596                     buttons[k].hide();
4597                 }
4598             }
4599         }
4600         return width;
4601     };
4602
4603     // private
4604     var handleEsc = function(d, k, e){
4605         if(opt && opt.closable !== false){
4606             dlg.hide();
4607         }
4608         if(e){
4609             e.stopEvent();
4610         }
4611     };
4612
4613     return {
4614         /**
4615          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4616          * @return {Roo.BasicDialog} The BasicDialog element
4617          */
4618         getDialog : function(){
4619            if(!dlg){
4620                 dlg = new Roo.bootstrap.Modal( {
4621                     //draggable: true,
4622                     //resizable:false,
4623                     //constraintoviewport:false,
4624                     //fixedcenter:true,
4625                     //collapsible : false,
4626                     //shim:true,
4627                     //modal: true,
4628                 //    width: 'auto',
4629                   //  height:100,
4630                     //buttonAlign:"center",
4631                     closeClick : function(){
4632                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4633                             handleButton("no");
4634                         }else{
4635                             handleButton("cancel");
4636                         }
4637                     }
4638                 });
4639                 dlg.render();
4640                 dlg.on("hide", handleHide);
4641                 mask = dlg.mask;
4642                 //dlg.addKeyListener(27, handleEsc);
4643                 buttons = {};
4644                 this.buttons = buttons;
4645                 var bt = this.buttonText;
4646                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4647                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4648                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4649                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4650                 //Roo.log(buttons);
4651                 bodyEl = dlg.bodyEl.createChild({
4652
4653                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4654                         '<textarea class="roo-mb-textarea"></textarea>' +
4655                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4656                 });
4657                 msgEl = bodyEl.dom.firstChild;
4658                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4659                 textboxEl.enableDisplayMode();
4660                 textboxEl.addKeyListener([10,13], function(){
4661                     if(dlg.isVisible() && opt && opt.buttons){
4662                         if(opt.buttons.ok){
4663                             handleButton("ok");
4664                         }else if(opt.buttons.yes){
4665                             handleButton("yes");
4666                         }
4667                     }
4668                 });
4669                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4670                 textareaEl.enableDisplayMode();
4671                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4672                 progressEl.enableDisplayMode();
4673                 
4674                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4675                 var pf = progressEl.dom.firstChild;
4676                 if (pf) {
4677                     pp = Roo.get(pf.firstChild);
4678                     pp.setHeight(pf.offsetHeight);
4679                 }
4680                 
4681             }
4682             return dlg;
4683         },
4684
4685         /**
4686          * Updates the message box body text
4687          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4688          * the XHTML-compliant non-breaking space character '&amp;#160;')
4689          * @return {Roo.MessageBox} This message box
4690          */
4691         updateText : function(text)
4692         {
4693             if(!dlg.isVisible() && !opt.width){
4694                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4695                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4696             }
4697             msgEl.innerHTML = text || '&#160;';
4698       
4699             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4700             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4701             var w = Math.max(
4702                     Math.min(opt.width || cw , this.maxWidth), 
4703                     Math.max(opt.minWidth || this.minWidth, bwidth)
4704             );
4705             if(opt.prompt){
4706                 activeTextEl.setWidth(w);
4707             }
4708             if(dlg.isVisible()){
4709                 dlg.fixedcenter = false;
4710             }
4711             // to big, make it scroll. = But as usual stupid IE does not support
4712             // !important..
4713             
4714             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4715                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4716                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4717             } else {
4718                 bodyEl.dom.style.height = '';
4719                 bodyEl.dom.style.overflowY = '';
4720             }
4721             if (cw > w) {
4722                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4723             } else {
4724                 bodyEl.dom.style.overflowX = '';
4725             }
4726             
4727             dlg.setContentSize(w, bodyEl.getHeight());
4728             if(dlg.isVisible()){
4729                 dlg.fixedcenter = true;
4730             }
4731             return this;
4732         },
4733
4734         /**
4735          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4736          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4737          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4738          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4739          * @return {Roo.MessageBox} This message box
4740          */
4741         updateProgress : function(value, text){
4742             if(text){
4743                 this.updateText(text);
4744             }
4745             
4746             if (pp) { // weird bug on my firefox - for some reason this is not defined
4747                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4748                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4749             }
4750             return this;
4751         },        
4752
4753         /**
4754          * Returns true if the message box is currently displayed
4755          * @return {Boolean} True if the message box is visible, else false
4756          */
4757         isVisible : function(){
4758             return dlg && dlg.isVisible();  
4759         },
4760
4761         /**
4762          * Hides the message box if it is displayed
4763          */
4764         hide : function(){
4765             if(this.isVisible()){
4766                 dlg.hide();
4767             }  
4768         },
4769
4770         /**
4771          * Displays a new message box, or reinitializes an existing message box, based on the config options
4772          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4773          * The following config object properties are supported:
4774          * <pre>
4775 Property    Type             Description
4776 ----------  ---------------  ------------------------------------------------------------------------------------
4777 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4778                                    closes (defaults to undefined)
4779 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4780                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4781 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4782                                    progress and wait dialogs will ignore this property and always hide the
4783                                    close button as they can only be closed programmatically.
4784 cls               String           A custom CSS class to apply to the message box element
4785 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4786                                    displayed (defaults to 75)
4787 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4788                                    function will be btn (the name of the button that was clicked, if applicable,
4789                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4790                                    Progress and wait dialogs will ignore this option since they do not respond to
4791                                    user actions and can only be closed programmatically, so any required function
4792                                    should be called by the same code after it closes the dialog.
4793 icon              String           A CSS class that provides a background image to be used as an icon for
4794                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4795 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4796 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4797 modal             Boolean          False to allow user interaction with the page while the message box is
4798                                    displayed (defaults to true)
4799 msg               String           A string that will replace the existing message box body text (defaults
4800                                    to the XHTML-compliant non-breaking space character '&#160;')
4801 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4802 progress          Boolean          True to display a progress bar (defaults to false)
4803 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4804 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4805 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4806 title             String           The title text
4807 value             String           The string value to set into the active textbox element if displayed
4808 wait              Boolean          True to display a progress bar (defaults to false)
4809 width             Number           The width of the dialog in pixels
4810 </pre>
4811          *
4812          * Example usage:
4813          * <pre><code>
4814 Roo.Msg.show({
4815    title: 'Address',
4816    msg: 'Please enter your address:',
4817    width: 300,
4818    buttons: Roo.MessageBox.OKCANCEL,
4819    multiline: true,
4820    fn: saveAddress,
4821    animEl: 'addAddressBtn'
4822 });
4823 </code></pre>
4824          * @param {Object} config Configuration options
4825          * @return {Roo.MessageBox} This message box
4826          */
4827         show : function(options)
4828         {
4829             
4830             // this causes nightmares if you show one dialog after another
4831             // especially on callbacks..
4832              
4833             if(this.isVisible()){
4834                 
4835                 this.hide();
4836                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4837                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4838                 Roo.log("New Dialog Message:" +  options.msg )
4839                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4840                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4841                 
4842             }
4843             var d = this.getDialog();
4844             opt = options;
4845             d.setTitle(opt.title || "&#160;");
4846             d.closeEl.setDisplayed(opt.closable !== false);
4847             activeTextEl = textboxEl;
4848             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4849             if(opt.prompt){
4850                 if(opt.multiline){
4851                     textboxEl.hide();
4852                     textareaEl.show();
4853                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4854                         opt.multiline : this.defaultTextHeight);
4855                     activeTextEl = textareaEl;
4856                 }else{
4857                     textboxEl.show();
4858                     textareaEl.hide();
4859                 }
4860             }else{
4861                 textboxEl.hide();
4862                 textareaEl.hide();
4863             }
4864             progressEl.setDisplayed(opt.progress === true);
4865             if (opt.progress) {
4866                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4867             }
4868             this.updateProgress(0);
4869             activeTextEl.dom.value = opt.value || "";
4870             if(opt.prompt){
4871                 dlg.setDefaultButton(activeTextEl);
4872             }else{
4873                 var bs = opt.buttons;
4874                 var db = null;
4875                 if(bs && bs.ok){
4876                     db = buttons["ok"];
4877                 }else if(bs && bs.yes){
4878                     db = buttons["yes"];
4879                 }
4880                 dlg.setDefaultButton(db);
4881             }
4882             bwidth = updateButtons(opt.buttons);
4883             this.updateText(opt.msg);
4884             if(opt.cls){
4885                 d.el.addClass(opt.cls);
4886             }
4887             d.proxyDrag = opt.proxyDrag === true;
4888             d.modal = opt.modal !== false;
4889             d.mask = opt.modal !== false ? mask : false;
4890             if(!d.isVisible()){
4891                 // force it to the end of the z-index stack so it gets a cursor in FF
4892                 document.body.appendChild(dlg.el.dom);
4893                 d.animateTarget = null;
4894                 d.show(options.animEl);
4895             }
4896             return this;
4897         },
4898
4899         /**
4900          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
4901          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
4902          * and closing the message box when the process is complete.
4903          * @param {String} title The title bar text
4904          * @param {String} msg The message box body text
4905          * @return {Roo.MessageBox} This message box
4906          */
4907         progress : function(title, msg){
4908             this.show({
4909                 title : title,
4910                 msg : msg,
4911                 buttons: false,
4912                 progress:true,
4913                 closable:false,
4914                 minWidth: this.minProgressWidth,
4915                 modal : true
4916             });
4917             return this;
4918         },
4919
4920         /**
4921          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
4922          * If a callback function is passed it will be called after the user clicks the button, and the
4923          * id of the button that was clicked will be passed as the only parameter to the callback
4924          * (could also be the top-right close button).
4925          * @param {String} title The title bar text
4926          * @param {String} msg The message box body text
4927          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4928          * @param {Object} scope (optional) The scope of the callback function
4929          * @return {Roo.MessageBox} This message box
4930          */
4931         alert : function(title, msg, fn, scope)
4932         {
4933             this.show({
4934                 title : title,
4935                 msg : msg,
4936                 buttons: this.OK,
4937                 fn: fn,
4938                 closable : false,
4939                 scope : scope,
4940                 modal : true
4941             });
4942             return this;
4943         },
4944
4945         /**
4946          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
4947          * interaction while waiting for a long-running process to complete that does not have defined intervals.
4948          * You are responsible for closing the message box when the process is complete.
4949          * @param {String} msg The message box body text
4950          * @param {String} title (optional) The title bar text
4951          * @return {Roo.MessageBox} This message box
4952          */
4953         wait : function(msg, title){
4954             this.show({
4955                 title : title,
4956                 msg : msg,
4957                 buttons: false,
4958                 closable:false,
4959                 progress:true,
4960                 modal:true,
4961                 width:300,
4962                 wait:true
4963             });
4964             waitTimer = Roo.TaskMgr.start({
4965                 run: function(i){
4966                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
4967                 },
4968                 interval: 1000
4969             });
4970             return this;
4971         },
4972
4973         /**
4974          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
4975          * If a callback function is passed it will be called after the user clicks either button, and the id of the
4976          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
4977          * @param {String} title The title bar text
4978          * @param {String} msg The message box body text
4979          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4980          * @param {Object} scope (optional) The scope of the callback function
4981          * @return {Roo.MessageBox} This message box
4982          */
4983         confirm : function(title, msg, fn, scope){
4984             this.show({
4985                 title : title,
4986                 msg : msg,
4987                 buttons: this.YESNO,
4988                 fn: fn,
4989                 scope : scope,
4990                 modal : true
4991             });
4992             return this;
4993         },
4994
4995         /**
4996          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
4997          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
4998          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
4999          * (could also be the top-right close button) and the text that was entered will be passed as the two
5000          * parameters to the callback.
5001          * @param {String} title The title bar text
5002          * @param {String} msg The message box body text
5003          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5004          * @param {Object} scope (optional) The scope of the callback function
5005          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5006          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5007          * @return {Roo.MessageBox} This message box
5008          */
5009         prompt : function(title, msg, fn, scope, multiline){
5010             this.show({
5011                 title : title,
5012                 msg : msg,
5013                 buttons: this.OKCANCEL,
5014                 fn: fn,
5015                 minWidth:250,
5016                 scope : scope,
5017                 prompt:true,
5018                 multiline: multiline,
5019                 modal : true
5020             });
5021             return this;
5022         },
5023
5024         /**
5025          * Button config that displays a single OK button
5026          * @type Object
5027          */
5028         OK : {ok:true},
5029         /**
5030          * Button config that displays Yes and No buttons
5031          * @type Object
5032          */
5033         YESNO : {yes:true, no:true},
5034         /**
5035          * Button config that displays OK and Cancel buttons
5036          * @type Object
5037          */
5038         OKCANCEL : {ok:true, cancel:true},
5039         /**
5040          * Button config that displays Yes, No and Cancel buttons
5041          * @type Object
5042          */
5043         YESNOCANCEL : {yes:true, no:true, cancel:true},
5044
5045         /**
5046          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5047          * @type Number
5048          */
5049         defaultTextHeight : 75,
5050         /**
5051          * The maximum width in pixels of the message box (defaults to 600)
5052          * @type Number
5053          */
5054         maxWidth : 600,
5055         /**
5056          * The minimum width in pixels of the message box (defaults to 100)
5057          * @type Number
5058          */
5059         minWidth : 100,
5060         /**
5061          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5062          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5063          * @type Number
5064          */
5065         minProgressWidth : 250,
5066         /**
5067          * An object containing the default button text strings that can be overriden for localized language support.
5068          * Supported properties are: ok, cancel, yes and no.
5069          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5070          * @type Object
5071          */
5072         buttonText : {
5073             ok : "OK",
5074             cancel : "Cancel",
5075             yes : "Yes",
5076             no : "No"
5077         }
5078     };
5079 }();
5080
5081 /**
5082  * Shorthand for {@link Roo.MessageBox}
5083  */
5084 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5085 Roo.Msg = Roo.Msg || Roo.MessageBox;
5086 /*
5087  * - LGPL
5088  *
5089  * navbar
5090  * 
5091  */
5092
5093 /**
5094  * @class Roo.bootstrap.Navbar
5095  * @extends Roo.bootstrap.Component
5096  * Bootstrap Navbar class
5097
5098  * @constructor
5099  * Create a new Navbar
5100  * @param {Object} config The config object
5101  */
5102
5103
5104 Roo.bootstrap.Navbar = function(config){
5105     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5106     this.addEvents({
5107         // raw events
5108         /**
5109          * @event beforetoggle
5110          * Fire before toggle the menu
5111          * @param {Roo.EventObject} e
5112          */
5113         "beforetoggle" : true
5114     });
5115 };
5116
5117 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5118     
5119     
5120    
5121     // private
5122     navItems : false,
5123     loadMask : false,
5124     
5125     
5126     getAutoCreate : function(){
5127         
5128         
5129         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5130         
5131     },
5132     
5133     initEvents :function ()
5134     {
5135         //Roo.log(this.el.select('.navbar-toggle',true));
5136         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5137         
5138         var mark = {
5139             tag: "div",
5140             cls:"x-dlg-mask"
5141         };
5142         
5143         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5144         
5145         var size = this.el.getSize();
5146         this.maskEl.setSize(size.width, size.height);
5147         this.maskEl.enableDisplayMode("block");
5148         this.maskEl.hide();
5149         
5150         if(this.loadMask){
5151             this.maskEl.show();
5152         }
5153     },
5154     
5155     
5156     getChildContainer : function()
5157     {
5158         if (this.el && this.el.select('.collapse').getCount()) {
5159             return this.el.select('.collapse',true).first();
5160         }
5161         
5162         return this.el;
5163     },
5164     
5165     mask : function()
5166     {
5167         this.maskEl.show();
5168     },
5169     
5170     unmask : function()
5171     {
5172         this.maskEl.hide();
5173     },
5174     onToggle : function()
5175     {
5176         
5177         if(this.fireEvent('beforetoggle', this) === false){
5178             return;
5179         }
5180         var ce = this.el.select('.navbar-collapse',true).first();
5181       
5182         if (!ce.hasClass('show')) {
5183            this.expand();
5184         } else {
5185             this.collapse();
5186         }
5187         
5188         
5189     
5190     },
5191     /**
5192      * Expand the navbar pulldown 
5193      */
5194     expand : function ()
5195     {
5196        
5197         var ce = this.el.select('.navbar-collapse',true).first();
5198         if (ce.hasClass('collapsing')) {
5199             return;
5200         }
5201         ce.dom.style.height = '';
5202                // show it...
5203         ce.addClass('in'); // old...
5204         ce.removeClass('collapse');
5205         ce.addClass('show');
5206         var h = ce.getHeight();
5207         Roo.log(h);
5208         ce.removeClass('show');
5209         // at this point we should be able to see it..
5210         ce.addClass('collapsing');
5211         
5212         ce.setHeight(0); // resize it ...
5213         ce.on('transitionend', function() {
5214             //Roo.log('done transition');
5215             ce.removeClass('collapsing');
5216             ce.addClass('show');
5217             ce.removeClass('collapse');
5218
5219             ce.dom.style.height = '';
5220         }, this, { single: true} );
5221         ce.setHeight(h);
5222         ce.dom.scrollTop = 0;
5223     },
5224     /**
5225      * Collapse the navbar pulldown 
5226      */
5227     collapse : function()
5228     {
5229          var ce = this.el.select('.navbar-collapse',true).first();
5230        
5231         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5232             // it's collapsed or collapsing..
5233             return;
5234         }
5235         ce.removeClass('in'); // old...
5236         ce.setHeight(ce.getHeight());
5237         ce.removeClass('show');
5238         ce.addClass('collapsing');
5239         
5240         ce.on('transitionend', function() {
5241             ce.dom.style.height = '';
5242             ce.removeClass('collapsing');
5243             ce.addClass('collapse');
5244         }, this, { single: true} );
5245         ce.setHeight(0);
5246     }
5247     
5248     
5249     
5250 });
5251
5252
5253
5254  
5255
5256  /*
5257  * - LGPL
5258  *
5259  * navbar
5260  * 
5261  */
5262
5263 /**
5264  * @class Roo.bootstrap.NavSimplebar
5265  * @extends Roo.bootstrap.Navbar
5266  * Bootstrap Sidebar class
5267  *
5268  * @cfg {Boolean} inverse is inverted color
5269  * 
5270  * @cfg {String} type (nav | pills | tabs)
5271  * @cfg {Boolean} arrangement stacked | justified
5272  * @cfg {String} align (left | right) alignment
5273  * 
5274  * @cfg {Boolean} main (true|false) main nav bar? default false
5275  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5276  * 
5277  * @cfg {String} tag (header|footer|nav|div) default is nav 
5278
5279  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5280  * 
5281  * 
5282  * @constructor
5283  * Create a new Sidebar
5284  * @param {Object} config The config object
5285  */
5286
5287
5288 Roo.bootstrap.NavSimplebar = function(config){
5289     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5290 };
5291
5292 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5293     
5294     inverse: false,
5295     
5296     type: false,
5297     arrangement: '',
5298     align : false,
5299     
5300     weight : 'light',
5301     
5302     main : false,
5303     
5304     
5305     tag : false,
5306     
5307     
5308     getAutoCreate : function(){
5309         
5310         
5311         var cfg = {
5312             tag : this.tag || 'div',
5313             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5314         };
5315         if (['light','white'].indexOf(this.weight) > -1) {
5316             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5317         }
5318         cfg.cls += ' bg-' + this.weight;
5319         
5320         if (this.inverse) {
5321             cfg.cls += ' navbar-inverse';
5322             
5323         }
5324         
5325         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5326         
5327         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5328             return cfg;
5329         }
5330         
5331         
5332     
5333         
5334         cfg.cn = [
5335             {
5336                 cls: 'nav nav-' + this.xtype,
5337                 tag : 'ul'
5338             }
5339         ];
5340         
5341          
5342         this.type = this.type || 'nav';
5343         if (['tabs','pills'].indexOf(this.type) != -1) {
5344             cfg.cn[0].cls += ' nav-' + this.type
5345         
5346         
5347         } else {
5348             if (this.type!=='nav') {
5349                 Roo.log('nav type must be nav/tabs/pills')
5350             }
5351             cfg.cn[0].cls += ' navbar-nav'
5352         }
5353         
5354         
5355         
5356         
5357         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5358             cfg.cn[0].cls += ' nav-' + this.arrangement;
5359         }
5360         
5361         
5362         if (this.align === 'right') {
5363             cfg.cn[0].cls += ' navbar-right';
5364         }
5365         
5366         
5367         
5368         
5369         return cfg;
5370     
5371         
5372     }
5373     
5374     
5375     
5376 });
5377
5378
5379
5380  
5381
5382  
5383        /*
5384  * - LGPL
5385  *
5386  * navbar
5387  * navbar-fixed-top
5388  * navbar-expand-md  fixed-top 
5389  */
5390
5391 /**
5392  * @class Roo.bootstrap.NavHeaderbar
5393  * @extends Roo.bootstrap.NavSimplebar
5394  * Bootstrap Sidebar class
5395  *
5396  * @cfg {String} brand what is brand
5397  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5398  * @cfg {String} brand_href href of the brand
5399  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5400  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5401  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5402  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5403  * 
5404  * @constructor
5405  * Create a new Sidebar
5406  * @param {Object} config The config object
5407  */
5408
5409
5410 Roo.bootstrap.NavHeaderbar = function(config){
5411     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5412       
5413 };
5414
5415 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5416     
5417     position: '',
5418     brand: '',
5419     brand_href: false,
5420     srButton : true,
5421     autohide : false,
5422     desktopCenter : false,
5423    
5424     
5425     getAutoCreate : function(){
5426         
5427         var   cfg = {
5428             tag: this.nav || 'nav',
5429             cls: 'navbar navbar-expand-md',
5430             role: 'navigation',
5431             cn: []
5432         };
5433         
5434         var cn = cfg.cn;
5435         if (this.desktopCenter) {
5436             cn.push({cls : 'container', cn : []});
5437             cn = cn[0].cn;
5438         }
5439         
5440         if(this.srButton){
5441             var btn = {
5442                 tag: 'button',
5443                 type: 'button',
5444                 cls: 'navbar-toggle navbar-toggler',
5445                 'data-toggle': 'collapse',
5446                 cn: [
5447                     {
5448                         tag: 'span',
5449                         cls: 'sr-only',
5450                         html: 'Toggle navigation'
5451                     },
5452                     {
5453                         tag: 'span',
5454                         cls: 'icon-bar navbar-toggler-icon'
5455                     },
5456                     {
5457                         tag: 'span',
5458                         cls: 'icon-bar'
5459                     },
5460                     {
5461                         tag: 'span',
5462                         cls: 'icon-bar'
5463                     }
5464                 ]
5465             };
5466             
5467             cn.push( Roo.bootstrap.version == 4 ? btn : {
5468                 tag: 'div',
5469                 cls: 'navbar-header',
5470                 cn: [
5471                     btn
5472                 ]
5473             });
5474         }
5475         
5476         cn.push({
5477             tag: 'div',
5478             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5479             cn : []
5480         });
5481         
5482         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5483         
5484         if (['light','white'].indexOf(this.weight) > -1) {
5485             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5486         }
5487         cfg.cls += ' bg-' + this.weight;
5488         
5489         
5490         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5491             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5492             
5493             // tag can override this..
5494             
5495             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5496         }
5497         
5498         if (this.brand !== '') {
5499             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5500             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5501                 tag: 'a',
5502                 href: this.brand_href ? this.brand_href : '#',
5503                 cls: 'navbar-brand',
5504                 cn: [
5505                 this.brand
5506                 ]
5507             });
5508         }
5509         
5510         if(this.main){
5511             cfg.cls += ' main-nav';
5512         }
5513         
5514         
5515         return cfg;
5516
5517         
5518     },
5519     getHeaderChildContainer : function()
5520     {
5521         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5522             return this.el.select('.navbar-header',true).first();
5523         }
5524         
5525         return this.getChildContainer();
5526     },
5527     
5528     getChildContainer : function()
5529     {
5530          
5531         return this.el.select('.roo-navbar-collapse',true).first();
5532          
5533         
5534     },
5535     
5536     initEvents : function()
5537     {
5538         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5539         
5540         if (this.autohide) {
5541             
5542             var prevScroll = 0;
5543             var ft = this.el;
5544             
5545             Roo.get(document).on('scroll',function(e) {
5546                 var ns = Roo.get(document).getScroll().top;
5547                 var os = prevScroll;
5548                 prevScroll = ns;
5549                 
5550                 if(ns > os){
5551                     ft.removeClass('slideDown');
5552                     ft.addClass('slideUp');
5553                     return;
5554                 }
5555                 ft.removeClass('slideUp');
5556                 ft.addClass('slideDown');
5557                  
5558               
5559           },this);
5560         }
5561     }    
5562     
5563 });
5564
5565
5566
5567  
5568
5569  /*
5570  * - LGPL
5571  *
5572  * navbar
5573  * 
5574  */
5575
5576 /**
5577  * @class Roo.bootstrap.NavSidebar
5578  * @extends Roo.bootstrap.Navbar
5579  * Bootstrap Sidebar class
5580  * 
5581  * @constructor
5582  * Create a new Sidebar
5583  * @param {Object} config The config object
5584  */
5585
5586
5587 Roo.bootstrap.NavSidebar = function(config){
5588     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5589 };
5590
5591 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5592     
5593     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5594     
5595     getAutoCreate : function(){
5596         
5597         
5598         return  {
5599             tag: 'div',
5600             cls: 'sidebar sidebar-nav'
5601         };
5602     
5603         
5604     }
5605     
5606     
5607     
5608 });
5609
5610
5611
5612  
5613
5614  /*
5615  * - LGPL
5616  *
5617  * nav group
5618  * 
5619  */
5620
5621 /**
5622  * @class Roo.bootstrap.NavGroup
5623  * @extends Roo.bootstrap.Component
5624  * Bootstrap NavGroup class
5625  * @cfg {String} align (left|right)
5626  * @cfg {Boolean} inverse
5627  * @cfg {String} type (nav|pills|tab) default nav
5628  * @cfg {String} navId - reference Id for navbar.
5629
5630  * 
5631  * @constructor
5632  * Create a new nav group
5633  * @param {Object} config The config object
5634  */
5635
5636 Roo.bootstrap.NavGroup = function(config){
5637     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5638     this.navItems = [];
5639    
5640     Roo.bootstrap.NavGroup.register(this);
5641      this.addEvents({
5642         /**
5643              * @event changed
5644              * Fires when the active item changes
5645              * @param {Roo.bootstrap.NavGroup} this
5646              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5647              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5648          */
5649         'changed': true
5650      });
5651     
5652 };
5653
5654 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5655     
5656     align: '',
5657     inverse: false,
5658     form: false,
5659     type: 'nav',
5660     navId : '',
5661     // private
5662     
5663     navItems : false, 
5664     
5665     getAutoCreate : function()
5666     {
5667         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5668         
5669         cfg = {
5670             tag : 'ul',
5671             cls: 'nav' 
5672         };
5673         if (Roo.bootstrap.version == 4) {
5674             if (['tabs','pills'].indexOf(this.type) != -1) {
5675                 cfg.cls += ' nav-' + this.type; 
5676             } else {
5677                 // trying to remove so header bar can right align top?
5678                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5679                     // do not use on header bar... 
5680                     cfg.cls += ' navbar-nav';
5681                 }
5682             }
5683             
5684         } else {
5685             if (['tabs','pills'].indexOf(this.type) != -1) {
5686                 cfg.cls += ' nav-' + this.type
5687             } else {
5688                 if (this.type !== 'nav') {
5689                     Roo.log('nav type must be nav/tabs/pills')
5690                 }
5691                 cfg.cls += ' navbar-nav'
5692             }
5693         }
5694         
5695         if (this.parent() && this.parent().sidebar) {
5696             cfg = {
5697                 tag: 'ul',
5698                 cls: 'dashboard-menu sidebar-menu'
5699             };
5700             
5701             return cfg;
5702         }
5703         
5704         if (this.form === true) {
5705             cfg = {
5706                 tag: 'form',
5707                 cls: 'navbar-form form-inline'
5708             };
5709             //nav navbar-right ml-md-auto
5710             if (this.align === 'right') {
5711                 cfg.cls += ' navbar-right ml-md-auto';
5712             } else {
5713                 cfg.cls += ' navbar-left';
5714             }
5715         }
5716         
5717         if (this.align === 'right') {
5718             cfg.cls += ' navbar-right ml-md-auto';
5719         } else {
5720             cfg.cls += ' mr-auto';
5721         }
5722         
5723         if (this.inverse) {
5724             cfg.cls += ' navbar-inverse';
5725             
5726         }
5727         
5728         
5729         return cfg;
5730     },
5731     /**
5732     * sets the active Navigation item
5733     * @param {Roo.bootstrap.NavItem} the new current navitem
5734     */
5735     setActiveItem : function(item)
5736     {
5737         var prev = false;
5738         Roo.each(this.navItems, function(v){
5739             if (v == item) {
5740                 return ;
5741             }
5742             if (v.isActive()) {
5743                 v.setActive(false, true);
5744                 prev = v;
5745                 
5746             }
5747             
5748         });
5749
5750         item.setActive(true, true);
5751         this.fireEvent('changed', this, item, prev);
5752         
5753         
5754     },
5755     /**
5756     * gets the active Navigation item
5757     * @return {Roo.bootstrap.NavItem} the current navitem
5758     */
5759     getActive : function()
5760     {
5761         
5762         var prev = false;
5763         Roo.each(this.navItems, function(v){
5764             
5765             if (v.isActive()) {
5766                 prev = v;
5767                 
5768             }
5769             
5770         });
5771         return prev;
5772     },
5773     
5774     indexOfNav : function()
5775     {
5776         
5777         var prev = false;
5778         Roo.each(this.navItems, function(v,i){
5779             
5780             if (v.isActive()) {
5781                 prev = i;
5782                 
5783             }
5784             
5785         });
5786         return prev;
5787     },
5788     /**
5789     * adds a Navigation item
5790     * @param {Roo.bootstrap.NavItem} the navitem to add
5791     */
5792     addItem : function(cfg)
5793     {
5794         if (this.form && Roo.bootstrap.version == 4) {
5795             cfg.tag = 'div';
5796         }
5797         var cn = new Roo.bootstrap.NavItem(cfg);
5798         this.register(cn);
5799         cn.parentId = this.id;
5800         cn.onRender(this.el, null);
5801         return cn;
5802     },
5803     /**
5804     * register a Navigation item
5805     * @param {Roo.bootstrap.NavItem} the navitem to add
5806     */
5807     register : function(item)
5808     {
5809         this.navItems.push( item);
5810         item.navId = this.navId;
5811     
5812     },
5813     
5814     /**
5815     * clear all the Navigation item
5816     */
5817    
5818     clearAll : function()
5819     {
5820         this.navItems = [];
5821         this.el.dom.innerHTML = '';
5822     },
5823     
5824     getNavItem: function(tabId)
5825     {
5826         var ret = false;
5827         Roo.each(this.navItems, function(e) {
5828             if (e.tabId == tabId) {
5829                ret =  e;
5830                return false;
5831             }
5832             return true;
5833             
5834         });
5835         return ret;
5836     },
5837     
5838     setActiveNext : function()
5839     {
5840         var i = this.indexOfNav(this.getActive());
5841         if (i > this.navItems.length) {
5842             return;
5843         }
5844         this.setActiveItem(this.navItems[i+1]);
5845     },
5846     setActivePrev : function()
5847     {
5848         var i = this.indexOfNav(this.getActive());
5849         if (i  < 1) {
5850             return;
5851         }
5852         this.setActiveItem(this.navItems[i-1]);
5853     },
5854     clearWasActive : function(except) {
5855         Roo.each(this.navItems, function(e) {
5856             if (e.tabId != except.tabId && e.was_active) {
5857                e.was_active = false;
5858                return false;
5859             }
5860             return true;
5861             
5862         });
5863     },
5864     getWasActive : function ()
5865     {
5866         var r = false;
5867         Roo.each(this.navItems, function(e) {
5868             if (e.was_active) {
5869                r = e;
5870                return false;
5871             }
5872             return true;
5873             
5874         });
5875         return r;
5876     }
5877     
5878     
5879 });
5880
5881  
5882 Roo.apply(Roo.bootstrap.NavGroup, {
5883     
5884     groups: {},
5885      /**
5886     * register a Navigation Group
5887     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5888     */
5889     register : function(navgrp)
5890     {
5891         this.groups[navgrp.navId] = navgrp;
5892         
5893     },
5894     /**
5895     * fetch a Navigation Group based on the navigation ID
5896     * @param {string} the navgroup to add
5897     * @returns {Roo.bootstrap.NavGroup} the navgroup 
5898     */
5899     get: function(navId) {
5900         if (typeof(this.groups[navId]) == 'undefined') {
5901             return false;
5902             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
5903         }
5904         return this.groups[navId] ;
5905     }
5906     
5907     
5908     
5909 });
5910
5911  /*
5912  * - LGPL
5913  *
5914  * row
5915  * 
5916  */
5917
5918 /**
5919  * @class Roo.bootstrap.NavItem
5920  * @extends Roo.bootstrap.Component
5921  * Bootstrap Navbar.NavItem class
5922  * @cfg {String} href  link to
5923  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
5924
5925  * @cfg {String} html content of button
5926  * @cfg {String} badge text inside badge
5927  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
5928  * @cfg {String} glyphicon DEPRICATED - use fa
5929  * @cfg {String} icon DEPRICATED - use fa
5930  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
5931  * @cfg {Boolean} active Is item active
5932  * @cfg {Boolean} disabled Is item disabled
5933  
5934  * @cfg {Boolean} preventDefault (true | false) default false
5935  * @cfg {String} tabId the tab that this item activates.
5936  * @cfg {String} tagtype (a|span) render as a href or span?
5937  * @cfg {Boolean} animateRef (true|false) link to element default false  
5938   
5939  * @constructor
5940  * Create a new Navbar Item
5941  * @param {Object} config The config object
5942  */
5943 Roo.bootstrap.NavItem = function(config){
5944     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
5945     this.addEvents({
5946         // raw events
5947         /**
5948          * @event click
5949          * The raw click event for the entire grid.
5950          * @param {Roo.EventObject} e
5951          */
5952         "click" : true,
5953          /**
5954             * @event changed
5955             * Fires when the active item active state changes
5956             * @param {Roo.bootstrap.NavItem} this
5957             * @param {boolean} state the new state
5958              
5959          */
5960         'changed': true,
5961         /**
5962             * @event scrollto
5963             * Fires when scroll to element
5964             * @param {Roo.bootstrap.NavItem} this
5965             * @param {Object} options
5966             * @param {Roo.EventObject} e
5967              
5968          */
5969         'scrollto': true
5970     });
5971    
5972 };
5973
5974 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
5975     
5976     href: false,
5977     html: '',
5978     badge: '',
5979     icon: false,
5980     fa : false,
5981     glyphicon: false,
5982     active: false,
5983     preventDefault : false,
5984     tabId : false,
5985     tagtype : 'a',
5986     tag: 'li',
5987     disabled : false,
5988     animateRef : false,
5989     was_active : false,
5990     button_weight : '',
5991     button_outline : false,
5992     
5993     navLink: false,
5994     
5995     getAutoCreate : function(){
5996          
5997         var cfg = {
5998             tag: this.tag,
5999             cls: 'nav-item'
6000         };
6001         
6002         if (this.active) {
6003             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6004         }
6005         if (this.disabled) {
6006             cfg.cls += ' disabled';
6007         }
6008         
6009         // BS4 only?
6010         if (this.button_weight.length) {
6011             cfg.tag = this.href ? 'a' : 'button';
6012             cfg.html = this.html || '';
6013             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6014             if (this.href) {
6015                 cfg.href = this.href;
6016             }
6017             if (this.fa) {
6018                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6019             }
6020             
6021             // menu .. should add dropdown-menu class - so no need for carat..
6022             
6023             if (this.badge !== '') {
6024                  
6025                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6026             }
6027             return cfg;
6028         }
6029         
6030         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6031             cfg.cn = [
6032                 {
6033                     tag: this.tagtype,
6034                     href : this.href || "#",
6035                     html: this.html || ''
6036                 }
6037             ];
6038             if (this.tagtype == 'a') {
6039                 cfg.cn[0].cls = 'nav-link';
6040             }
6041             if (this.icon) {
6042                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6043             }
6044             if (this.fa) {
6045                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6046             }
6047             if(this.glyphicon) {
6048                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6049             }
6050             
6051             if (this.menu) {
6052                 
6053                 cfg.cn[0].html += " <span class='caret'></span>";
6054              
6055             }
6056             
6057             if (this.badge !== '') {
6058                  
6059                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6060             }
6061         }
6062         
6063         
6064         
6065         return cfg;
6066     },
6067     onRender : function(ct, position)
6068     {
6069        // Roo.log("Call onRender: " + this.xtype);
6070         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6071             this.tag = 'div';
6072         }
6073         
6074         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6075         this.navLink = this.el.select('.nav-link',true).first();
6076         return ret;
6077     },
6078       
6079     
6080     initEvents: function() 
6081     {
6082         if (typeof (this.menu) != 'undefined') {
6083             this.menu.parentType = this.xtype;
6084             this.menu.triggerEl = this.el;
6085             this.menu = this.addxtype(Roo.apply({}, this.menu));
6086         }
6087         
6088         this.el.select('a',true).on('click', this.onClick, this);
6089         
6090         if(this.tagtype == 'span'){
6091             this.el.select('span',true).on('click', this.onClick, this);
6092         }
6093        
6094         // at this point parent should be available..
6095         this.parent().register(this);
6096     },
6097     
6098     onClick : function(e)
6099     {
6100         if (e.getTarget('.dropdown-menu-item')) {
6101             // did you click on a menu itemm.... - then don't trigger onclick..
6102             return;
6103         }
6104         
6105         if(
6106                 this.preventDefault || 
6107                 this.href == '#' 
6108         ){
6109             Roo.log("NavItem - prevent Default?");
6110             e.preventDefault();
6111         }
6112         
6113         if (this.disabled) {
6114             return;
6115         }
6116         
6117         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6118         if (tg && tg.transition) {
6119             Roo.log("waiting for the transitionend");
6120             return;
6121         }
6122         
6123         
6124         
6125         //Roo.log("fire event clicked");
6126         if(this.fireEvent('click', this, e) === false){
6127             return;
6128         };
6129         
6130         if(this.tagtype == 'span'){
6131             return;
6132         }
6133         
6134         //Roo.log(this.href);
6135         var ael = this.el.select('a',true).first();
6136         //Roo.log(ael);
6137         
6138         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6139             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6140             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6141                 return; // ignore... - it's a 'hash' to another page.
6142             }
6143             Roo.log("NavItem - prevent Default?");
6144             e.preventDefault();
6145             this.scrollToElement(e);
6146         }
6147         
6148         
6149         var p =  this.parent();
6150    
6151         if (['tabs','pills'].indexOf(p.type)!==-1) {
6152             if (typeof(p.setActiveItem) !== 'undefined') {
6153                 p.setActiveItem(this);
6154             }
6155         }
6156         
6157         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6158         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6159             // remove the collapsed menu expand...
6160             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6161         }
6162     },
6163     
6164     isActive: function () {
6165         return this.active
6166     },
6167     setActive : function(state, fire, is_was_active)
6168     {
6169         if (this.active && !state && this.navId) {
6170             this.was_active = true;
6171             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6172             if (nv) {
6173                 nv.clearWasActive(this);
6174             }
6175             
6176         }
6177         this.active = state;
6178         
6179         if (!state ) {
6180             this.el.removeClass('active');
6181             this.navLink ? this.navLink.removeClass('active') : false;
6182         } else if (!this.el.hasClass('active')) {
6183             
6184             this.el.addClass('active');
6185             if (Roo.bootstrap.version == 4 && this.navLink ) {
6186                 this.navLink.addClass('active');
6187             }
6188             
6189         }
6190         if (fire) {
6191             this.fireEvent('changed', this, state);
6192         }
6193         
6194         // show a panel if it's registered and related..
6195         
6196         if (!this.navId || !this.tabId || !state || is_was_active) {
6197             return;
6198         }
6199         
6200         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6201         if (!tg) {
6202             return;
6203         }
6204         var pan = tg.getPanelByName(this.tabId);
6205         if (!pan) {
6206             return;
6207         }
6208         // if we can not flip to new panel - go back to old nav highlight..
6209         if (false == tg.showPanel(pan)) {
6210             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6211             if (nv) {
6212                 var onav = nv.getWasActive();
6213                 if (onav) {
6214                     onav.setActive(true, false, true);
6215                 }
6216             }
6217             
6218         }
6219         
6220         
6221         
6222     },
6223      // this should not be here...
6224     setDisabled : function(state)
6225     {
6226         this.disabled = state;
6227         if (!state ) {
6228             this.el.removeClass('disabled');
6229         } else if (!this.el.hasClass('disabled')) {
6230             this.el.addClass('disabled');
6231         }
6232         
6233     },
6234     
6235     /**
6236      * Fetch the element to display the tooltip on.
6237      * @return {Roo.Element} defaults to this.el
6238      */
6239     tooltipEl : function()
6240     {
6241         return this.el.select('' + this.tagtype + '', true).first();
6242     },
6243     
6244     scrollToElement : function(e)
6245     {
6246         var c = document.body;
6247         
6248         /*
6249          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6250          */
6251         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6252             c = document.documentElement;
6253         }
6254         
6255         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6256         
6257         if(!target){
6258             return;
6259         }
6260
6261         var o = target.calcOffsetsTo(c);
6262         
6263         var options = {
6264             target : target,
6265             value : o[1]
6266         };
6267         
6268         this.fireEvent('scrollto', this, options, e);
6269         
6270         Roo.get(c).scrollTo('top', options.value, true);
6271         
6272         return;
6273     }
6274 });
6275  
6276
6277  /*
6278  * - LGPL
6279  *
6280  * sidebar item
6281  *
6282  *  li
6283  *    <span> icon </span>
6284  *    <span> text </span>
6285  *    <span>badge </span>
6286  */
6287
6288 /**
6289  * @class Roo.bootstrap.NavSidebarItem
6290  * @extends Roo.bootstrap.NavItem
6291  * Bootstrap Navbar.NavSidebarItem class
6292  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6293  * {Boolean} open is the menu open
6294  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6295  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6296  * {String} buttonSize (sm|md|lg)the extra classes for the button
6297  * {Boolean} showArrow show arrow next to the text (default true)
6298  * @constructor
6299  * Create a new Navbar Button
6300  * @param {Object} config The config object
6301  */
6302 Roo.bootstrap.NavSidebarItem = function(config){
6303     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6304     this.addEvents({
6305         // raw events
6306         /**
6307          * @event click
6308          * The raw click event for the entire grid.
6309          * @param {Roo.EventObject} e
6310          */
6311         "click" : true,
6312          /**
6313             * @event changed
6314             * Fires when the active item active state changes
6315             * @param {Roo.bootstrap.NavSidebarItem} this
6316             * @param {boolean} state the new state
6317              
6318          */
6319         'changed': true
6320     });
6321    
6322 };
6323
6324 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6325     
6326     badgeWeight : 'default',
6327     
6328     open: false,
6329     
6330     buttonView : false,
6331     
6332     buttonWeight : 'default',
6333     
6334     buttonSize : 'md',
6335     
6336     showArrow : true,
6337     
6338     getAutoCreate : function(){
6339         
6340         
6341         var a = {
6342                 tag: 'a',
6343                 href : this.href || '#',
6344                 cls: '',
6345                 html : '',
6346                 cn : []
6347         };
6348         
6349         if(this.buttonView){
6350             a = {
6351                 tag: 'button',
6352                 href : this.href || '#',
6353                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6354                 html : this.html,
6355                 cn : []
6356             };
6357         }
6358         
6359         var cfg = {
6360             tag: 'li',
6361             cls: '',
6362             cn: [ a ]
6363         };
6364         
6365         if (this.active) {
6366             cfg.cls += ' active';
6367         }
6368         
6369         if (this.disabled) {
6370             cfg.cls += ' disabled';
6371         }
6372         if (this.open) {
6373             cfg.cls += ' open x-open';
6374         }
6375         // left icon..
6376         if (this.glyphicon || this.icon) {
6377             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6378             a.cn.push({ tag : 'i', cls : c }) ;
6379         }
6380         
6381         if(!this.buttonView){
6382             var span = {
6383                 tag: 'span',
6384                 html : this.html || ''
6385             };
6386
6387             a.cn.push(span);
6388             
6389         }
6390         
6391         if (this.badge !== '') {
6392             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6393         }
6394         
6395         if (this.menu) {
6396             
6397             if(this.showArrow){
6398                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6399             }
6400             
6401             a.cls += ' dropdown-toggle treeview' ;
6402         }
6403         
6404         return cfg;
6405     },
6406     
6407     initEvents : function()
6408     { 
6409         if (typeof (this.menu) != 'undefined') {
6410             this.menu.parentType = this.xtype;
6411             this.menu.triggerEl = this.el;
6412             this.menu = this.addxtype(Roo.apply({}, this.menu));
6413         }
6414         
6415         this.el.on('click', this.onClick, this);
6416         
6417         if(this.badge !== ''){
6418             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6419         }
6420         
6421     },
6422     
6423     onClick : function(e)
6424     {
6425         if(this.disabled){
6426             e.preventDefault();
6427             return;
6428         }
6429         
6430         if(this.preventDefault){
6431             e.preventDefault();
6432         }
6433         
6434         this.fireEvent('click', this, e);
6435     },
6436     
6437     disable : function()
6438     {
6439         this.setDisabled(true);
6440     },
6441     
6442     enable : function()
6443     {
6444         this.setDisabled(false);
6445     },
6446     
6447     setDisabled : function(state)
6448     {
6449         if(this.disabled == state){
6450             return;
6451         }
6452         
6453         this.disabled = state;
6454         
6455         if (state) {
6456             this.el.addClass('disabled');
6457             return;
6458         }
6459         
6460         this.el.removeClass('disabled');
6461         
6462         return;
6463     },
6464     
6465     setActive : function(state)
6466     {
6467         if(this.active == state){
6468             return;
6469         }
6470         
6471         this.active = state;
6472         
6473         if (state) {
6474             this.el.addClass('active');
6475             return;
6476         }
6477         
6478         this.el.removeClass('active');
6479         
6480         return;
6481     },
6482     
6483     isActive: function () 
6484     {
6485         return this.active;
6486     },
6487     
6488     setBadge : function(str)
6489     {
6490         if(!this.badgeEl){
6491             return;
6492         }
6493         
6494         this.badgeEl.dom.innerHTML = str;
6495     }
6496     
6497    
6498      
6499  
6500 });
6501  
6502
6503  /*
6504  * - LGPL
6505  *
6506  * row
6507  * 
6508  */
6509
6510 /**
6511  * @class Roo.bootstrap.Row
6512  * @extends Roo.bootstrap.Component
6513  * Bootstrap Row class (contains columns...)
6514  * 
6515  * @constructor
6516  * Create a new Row
6517  * @param {Object} config The config object
6518  */
6519
6520 Roo.bootstrap.Row = function(config){
6521     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6522 };
6523
6524 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6525     
6526     getAutoCreate : function(){
6527        return {
6528             cls: 'row clearfix'
6529        };
6530     }
6531     
6532     
6533 });
6534
6535  
6536
6537  /*
6538  * - LGPL
6539  *
6540  * pagination
6541  * 
6542  */
6543
6544 /**
6545  * @class Roo.bootstrap.Pagination
6546  * @extends Roo.bootstrap.Component
6547  * Bootstrap Pagination class
6548  * @cfg {String} size xs | sm | md | lg
6549  * @cfg {Boolean} inverse false | true
6550  * 
6551  * @constructor
6552  * Create a new Pagination
6553  * @param {Object} config The config object
6554  */
6555
6556 Roo.bootstrap.Pagination = function(config){
6557     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6558 };
6559
6560 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6561     
6562     cls: false,
6563     size: false,
6564     inverse: false,
6565     
6566     getAutoCreate : function(){
6567         var cfg = {
6568             tag: 'ul',
6569                 cls: 'pagination'
6570         };
6571         if (this.inverse) {
6572             cfg.cls += ' inverse';
6573         }
6574         if (this.html) {
6575             cfg.html=this.html;
6576         }
6577         if (this.cls) {
6578             cfg.cls += " " + this.cls;
6579         }
6580         return cfg;
6581     }
6582    
6583 });
6584
6585  
6586
6587  /*
6588  * - LGPL
6589  *
6590  * Pagination item
6591  * 
6592  */
6593
6594
6595 /**
6596  * @class Roo.bootstrap.PaginationItem
6597  * @extends Roo.bootstrap.Component
6598  * Bootstrap PaginationItem class
6599  * @cfg {String} html text
6600  * @cfg {String} href the link
6601  * @cfg {Boolean} preventDefault (true | false) default true
6602  * @cfg {Boolean} active (true | false) default false
6603  * @cfg {Boolean} disabled default false
6604  * 
6605  * 
6606  * @constructor
6607  * Create a new PaginationItem
6608  * @param {Object} config The config object
6609  */
6610
6611
6612 Roo.bootstrap.PaginationItem = function(config){
6613     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6614     this.addEvents({
6615         // raw events
6616         /**
6617          * @event click
6618          * The raw click event for the entire grid.
6619          * @param {Roo.EventObject} e
6620          */
6621         "click" : true
6622     });
6623 };
6624
6625 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6626     
6627     href : false,
6628     html : false,
6629     preventDefault: true,
6630     active : false,
6631     cls : false,
6632     disabled: false,
6633     
6634     getAutoCreate : function(){
6635         var cfg= {
6636             tag: 'li',
6637             cn: [
6638                 {
6639                     tag : 'a',
6640                     href : this.href ? this.href : '#',
6641                     html : this.html ? this.html : ''
6642                 }
6643             ]
6644         };
6645         
6646         if(this.cls){
6647             cfg.cls = this.cls;
6648         }
6649         
6650         if(this.disabled){
6651             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6652         }
6653         
6654         if(this.active){
6655             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6656         }
6657         
6658         return cfg;
6659     },
6660     
6661     initEvents: function() {
6662         
6663         this.el.on('click', this.onClick, this);
6664         
6665     },
6666     onClick : function(e)
6667     {
6668         Roo.log('PaginationItem on click ');
6669         if(this.preventDefault){
6670             e.preventDefault();
6671         }
6672         
6673         if(this.disabled){
6674             return;
6675         }
6676         
6677         this.fireEvent('click', this, e);
6678     }
6679    
6680 });
6681
6682  
6683
6684  /*
6685  * - LGPL
6686  *
6687  * slider
6688  * 
6689  */
6690
6691
6692 /**
6693  * @class Roo.bootstrap.Slider
6694  * @extends Roo.bootstrap.Component
6695  * Bootstrap Slider class
6696  *    
6697  * @constructor
6698  * Create a new Slider
6699  * @param {Object} config The config object
6700  */
6701
6702 Roo.bootstrap.Slider = function(config){
6703     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6704 };
6705
6706 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6707     
6708     getAutoCreate : function(){
6709         
6710         var cfg = {
6711             tag: 'div',
6712             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6713             cn: [
6714                 {
6715                     tag: 'a',
6716                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6717                 }
6718             ]
6719         };
6720         
6721         return cfg;
6722     }
6723    
6724 });
6725
6726  /*
6727  * Based on:
6728  * Ext JS Library 1.1.1
6729  * Copyright(c) 2006-2007, Ext JS, LLC.
6730  *
6731  * Originally Released Under LGPL - original licence link has changed is not relivant.
6732  *
6733  * Fork - LGPL
6734  * <script type="text/javascript">
6735  */
6736  
6737
6738 /**
6739  * @class Roo.grid.ColumnModel
6740  * @extends Roo.util.Observable
6741  * This is the default implementation of a ColumnModel used by the Grid. It defines
6742  * the columns in the grid.
6743  * <br>Usage:<br>
6744  <pre><code>
6745  var colModel = new Roo.grid.ColumnModel([
6746         {header: "Ticker", width: 60, sortable: true, locked: true},
6747         {header: "Company Name", width: 150, sortable: true},
6748         {header: "Market Cap.", width: 100, sortable: true},
6749         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6750         {header: "Employees", width: 100, sortable: true, resizable: false}
6751  ]);
6752  </code></pre>
6753  * <p>
6754  
6755  * The config options listed for this class are options which may appear in each
6756  * individual column definition.
6757  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6758  * @constructor
6759  * @param {Object} config An Array of column config objects. See this class's
6760  * config objects for details.
6761 */
6762 Roo.grid.ColumnModel = function(config){
6763         /**
6764      * The config passed into the constructor
6765      */
6766     this.config = config;
6767     this.lookup = {};
6768
6769     // if no id, create one
6770     // if the column does not have a dataIndex mapping,
6771     // map it to the order it is in the config
6772     for(var i = 0, len = config.length; i < len; i++){
6773         var c = config[i];
6774         if(typeof c.dataIndex == "undefined"){
6775             c.dataIndex = i;
6776         }
6777         if(typeof c.renderer == "string"){
6778             c.renderer = Roo.util.Format[c.renderer];
6779         }
6780         if(typeof c.id == "undefined"){
6781             c.id = Roo.id();
6782         }
6783         if(c.editor && c.editor.xtype){
6784             c.editor  = Roo.factory(c.editor, Roo.grid);
6785         }
6786         if(c.editor && c.editor.isFormField){
6787             c.editor = new Roo.grid.GridEditor(c.editor);
6788         }
6789         this.lookup[c.id] = c;
6790     }
6791
6792     /**
6793      * The width of columns which have no width specified (defaults to 100)
6794      * @type Number
6795      */
6796     this.defaultWidth = 100;
6797
6798     /**
6799      * Default sortable of columns which have no sortable specified (defaults to false)
6800      * @type Boolean
6801      */
6802     this.defaultSortable = false;
6803
6804     this.addEvents({
6805         /**
6806              * @event widthchange
6807              * Fires when the width of a column changes.
6808              * @param {ColumnModel} this
6809              * @param {Number} columnIndex The column index
6810              * @param {Number} newWidth The new width
6811              */
6812             "widthchange": true,
6813         /**
6814              * @event headerchange
6815              * Fires when the text of a header changes.
6816              * @param {ColumnModel} this
6817              * @param {Number} columnIndex The column index
6818              * @param {Number} newText The new header text
6819              */
6820             "headerchange": true,
6821         /**
6822              * @event hiddenchange
6823              * Fires when a column is hidden or "unhidden".
6824              * @param {ColumnModel} this
6825              * @param {Number} columnIndex The column index
6826              * @param {Boolean} hidden true if hidden, false otherwise
6827              */
6828             "hiddenchange": true,
6829             /**
6830          * @event columnmoved
6831          * Fires when a column is moved.
6832          * @param {ColumnModel} this
6833          * @param {Number} oldIndex
6834          * @param {Number} newIndex
6835          */
6836         "columnmoved" : true,
6837         /**
6838          * @event columlockchange
6839          * Fires when a column's locked state is changed
6840          * @param {ColumnModel} this
6841          * @param {Number} colIndex
6842          * @param {Boolean} locked true if locked
6843          */
6844         "columnlockchange" : true
6845     });
6846     Roo.grid.ColumnModel.superclass.constructor.call(this);
6847 };
6848 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6849     /**
6850      * @cfg {String} header The header text to display in the Grid view.
6851      */
6852     /**
6853      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6854      * {@link Roo.data.Record} definition from which to draw the column's value. If not
6855      * specified, the column's index is used as an index into the Record's data Array.
6856      */
6857     /**
6858      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6859      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6860      */
6861     /**
6862      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6863      * Defaults to the value of the {@link #defaultSortable} property.
6864      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6865      */
6866     /**
6867      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
6868      */
6869     /**
6870      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
6871      */
6872     /**
6873      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6874      */
6875     /**
6876      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6877      */
6878     /**
6879      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6880      * given the cell's data value. See {@link #setRenderer}. If not specified, the
6881      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6882      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6883      */
6884        /**
6885      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
6886      */
6887     /**
6888      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
6889      */
6890     /**
6891      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
6892      */
6893     /**
6894      * @cfg {String} cursor (Optional)
6895      */
6896     /**
6897      * @cfg {String} tooltip (Optional)
6898      */
6899     /**
6900      * @cfg {Number} xs (Optional)
6901      */
6902     /**
6903      * @cfg {Number} sm (Optional)
6904      */
6905     /**
6906      * @cfg {Number} md (Optional)
6907      */
6908     /**
6909      * @cfg {Number} lg (Optional)
6910      */
6911     /**
6912      * Returns the id of the column at the specified index.
6913      * @param {Number} index The column index
6914      * @return {String} the id
6915      */
6916     getColumnId : function(index){
6917         return this.config[index].id;
6918     },
6919
6920     /**
6921      * Returns the column for a specified id.
6922      * @param {String} id The column id
6923      * @return {Object} the column
6924      */
6925     getColumnById : function(id){
6926         return this.lookup[id];
6927     },
6928
6929     
6930     /**
6931      * Returns the column for a specified dataIndex.
6932      * @param {String} dataIndex The column dataIndex
6933      * @return {Object|Boolean} the column or false if not found
6934      */
6935     getColumnByDataIndex: function(dataIndex){
6936         var index = this.findColumnIndex(dataIndex);
6937         return index > -1 ? this.config[index] : false;
6938     },
6939     
6940     /**
6941      * Returns the index for a specified column id.
6942      * @param {String} id The column id
6943      * @return {Number} the index, or -1 if not found
6944      */
6945     getIndexById : function(id){
6946         for(var i = 0, len = this.config.length; i < len; i++){
6947             if(this.config[i].id == id){
6948                 return i;
6949             }
6950         }
6951         return -1;
6952     },
6953     
6954     /**
6955      * Returns the index for a specified column dataIndex.
6956      * @param {String} dataIndex The column dataIndex
6957      * @return {Number} the index, or -1 if not found
6958      */
6959     
6960     findColumnIndex : function(dataIndex){
6961         for(var i = 0, len = this.config.length; i < len; i++){
6962             if(this.config[i].dataIndex == dataIndex){
6963                 return i;
6964             }
6965         }
6966         return -1;
6967     },
6968     
6969     
6970     moveColumn : function(oldIndex, newIndex){
6971         var c = this.config[oldIndex];
6972         this.config.splice(oldIndex, 1);
6973         this.config.splice(newIndex, 0, c);
6974         this.dataMap = null;
6975         this.fireEvent("columnmoved", this, oldIndex, newIndex);
6976     },
6977
6978     isLocked : function(colIndex){
6979         return this.config[colIndex].locked === true;
6980     },
6981
6982     setLocked : function(colIndex, value, suppressEvent){
6983         if(this.isLocked(colIndex) == value){
6984             return;
6985         }
6986         this.config[colIndex].locked = value;
6987         if(!suppressEvent){
6988             this.fireEvent("columnlockchange", this, colIndex, value);
6989         }
6990     },
6991
6992     getTotalLockedWidth : function(){
6993         var totalWidth = 0;
6994         for(var i = 0; i < this.config.length; i++){
6995             if(this.isLocked(i) && !this.isHidden(i)){
6996                 this.totalWidth += this.getColumnWidth(i);
6997             }
6998         }
6999         return totalWidth;
7000     },
7001
7002     getLockedCount : function(){
7003         for(var i = 0, len = this.config.length; i < len; i++){
7004             if(!this.isLocked(i)){
7005                 return i;
7006             }
7007         }
7008         
7009         return this.config.length;
7010     },
7011
7012     /**
7013      * Returns the number of columns.
7014      * @return {Number}
7015      */
7016     getColumnCount : function(visibleOnly){
7017         if(visibleOnly === true){
7018             var c = 0;
7019             for(var i = 0, len = this.config.length; i < len; i++){
7020                 if(!this.isHidden(i)){
7021                     c++;
7022                 }
7023             }
7024             return c;
7025         }
7026         return this.config.length;
7027     },
7028
7029     /**
7030      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7031      * @param {Function} fn
7032      * @param {Object} scope (optional)
7033      * @return {Array} result
7034      */
7035     getColumnsBy : function(fn, scope){
7036         var r = [];
7037         for(var i = 0, len = this.config.length; i < len; i++){
7038             var c = this.config[i];
7039             if(fn.call(scope||this, c, i) === true){
7040                 r[r.length] = c;
7041             }
7042         }
7043         return r;
7044     },
7045
7046     /**
7047      * Returns true if the specified column is sortable.
7048      * @param {Number} col The column index
7049      * @return {Boolean}
7050      */
7051     isSortable : function(col){
7052         if(typeof this.config[col].sortable == "undefined"){
7053             return this.defaultSortable;
7054         }
7055         return this.config[col].sortable;
7056     },
7057
7058     /**
7059      * Returns the rendering (formatting) function defined for the column.
7060      * @param {Number} col The column index.
7061      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7062      */
7063     getRenderer : function(col){
7064         if(!this.config[col].renderer){
7065             return Roo.grid.ColumnModel.defaultRenderer;
7066         }
7067         return this.config[col].renderer;
7068     },
7069
7070     /**
7071      * Sets the rendering (formatting) function for a column.
7072      * @param {Number} col The column index
7073      * @param {Function} fn The function to use to process the cell's raw data
7074      * to return HTML markup for the grid view. The render function is called with
7075      * the following parameters:<ul>
7076      * <li>Data value.</li>
7077      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7078      * <li>css A CSS style string to apply to the table cell.</li>
7079      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7080      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7081      * <li>Row index</li>
7082      * <li>Column index</li>
7083      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7084      */
7085     setRenderer : function(col, fn){
7086         this.config[col].renderer = fn;
7087     },
7088
7089     /**
7090      * Returns the width for the specified column.
7091      * @param {Number} col The column index
7092      * @return {Number}
7093      */
7094     getColumnWidth : function(col){
7095         return this.config[col].width * 1 || this.defaultWidth;
7096     },
7097
7098     /**
7099      * Sets the width for a column.
7100      * @param {Number} col The column index
7101      * @param {Number} width The new width
7102      */
7103     setColumnWidth : function(col, width, suppressEvent){
7104         this.config[col].width = width;
7105         this.totalWidth = null;
7106         if(!suppressEvent){
7107              this.fireEvent("widthchange", this, col, width);
7108         }
7109     },
7110
7111     /**
7112      * Returns the total width of all columns.
7113      * @param {Boolean} includeHidden True to include hidden column widths
7114      * @return {Number}
7115      */
7116     getTotalWidth : function(includeHidden){
7117         if(!this.totalWidth){
7118             this.totalWidth = 0;
7119             for(var i = 0, len = this.config.length; i < len; i++){
7120                 if(includeHidden || !this.isHidden(i)){
7121                     this.totalWidth += this.getColumnWidth(i);
7122                 }
7123             }
7124         }
7125         return this.totalWidth;
7126     },
7127
7128     /**
7129      * Returns the header for the specified column.
7130      * @param {Number} col The column index
7131      * @return {String}
7132      */
7133     getColumnHeader : function(col){
7134         return this.config[col].header;
7135     },
7136
7137     /**
7138      * Sets the header for a column.
7139      * @param {Number} col The column index
7140      * @param {String} header The new header
7141      */
7142     setColumnHeader : function(col, header){
7143         this.config[col].header = header;
7144         this.fireEvent("headerchange", this, col, header);
7145     },
7146
7147     /**
7148      * Returns the tooltip for the specified column.
7149      * @param {Number} col The column index
7150      * @return {String}
7151      */
7152     getColumnTooltip : function(col){
7153             return this.config[col].tooltip;
7154     },
7155     /**
7156      * Sets the tooltip for a column.
7157      * @param {Number} col The column index
7158      * @param {String} tooltip The new tooltip
7159      */
7160     setColumnTooltip : function(col, tooltip){
7161             this.config[col].tooltip = tooltip;
7162     },
7163
7164     /**
7165      * Returns the dataIndex for the specified column.
7166      * @param {Number} col The column index
7167      * @return {Number}
7168      */
7169     getDataIndex : function(col){
7170         return this.config[col].dataIndex;
7171     },
7172
7173     /**
7174      * Sets the dataIndex for a column.
7175      * @param {Number} col The column index
7176      * @param {Number} dataIndex The new dataIndex
7177      */
7178     setDataIndex : function(col, dataIndex){
7179         this.config[col].dataIndex = dataIndex;
7180     },
7181
7182     
7183     
7184     /**
7185      * Returns true if the cell is editable.
7186      * @param {Number} colIndex The column index
7187      * @param {Number} rowIndex The row index - this is nto actually used..?
7188      * @return {Boolean}
7189      */
7190     isCellEditable : function(colIndex, rowIndex){
7191         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7192     },
7193
7194     /**
7195      * Returns the editor defined for the cell/column.
7196      * return false or null to disable editing.
7197      * @param {Number} colIndex The column index
7198      * @param {Number} rowIndex The row index
7199      * @return {Object}
7200      */
7201     getCellEditor : function(colIndex, rowIndex){
7202         return this.config[colIndex].editor;
7203     },
7204
7205     /**
7206      * Sets if a column is editable.
7207      * @param {Number} col The column index
7208      * @param {Boolean} editable True if the column is editable
7209      */
7210     setEditable : function(col, editable){
7211         this.config[col].editable = editable;
7212     },
7213
7214
7215     /**
7216      * Returns true if the column is hidden.
7217      * @param {Number} colIndex The column index
7218      * @return {Boolean}
7219      */
7220     isHidden : function(colIndex){
7221         return this.config[colIndex].hidden;
7222     },
7223
7224
7225     /**
7226      * Returns true if the column width cannot be changed
7227      */
7228     isFixed : function(colIndex){
7229         return this.config[colIndex].fixed;
7230     },
7231
7232     /**
7233      * Returns true if the column can be resized
7234      * @return {Boolean}
7235      */
7236     isResizable : function(colIndex){
7237         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7238     },
7239     /**
7240      * Sets if a column is hidden.
7241      * @param {Number} colIndex The column index
7242      * @param {Boolean} hidden True if the column is hidden
7243      */
7244     setHidden : function(colIndex, hidden){
7245         this.config[colIndex].hidden = hidden;
7246         this.totalWidth = null;
7247         this.fireEvent("hiddenchange", this, colIndex, hidden);
7248     },
7249
7250     /**
7251      * Sets the editor for a column.
7252      * @param {Number} col The column index
7253      * @param {Object} editor The editor object
7254      */
7255     setEditor : function(col, editor){
7256         this.config[col].editor = editor;
7257     }
7258 });
7259
7260 Roo.grid.ColumnModel.defaultRenderer = function(value)
7261 {
7262     if(typeof value == "object") {
7263         return value;
7264     }
7265         if(typeof value == "string" && value.length < 1){
7266             return "&#160;";
7267         }
7268     
7269         return String.format("{0}", value);
7270 };
7271
7272 // Alias for backwards compatibility
7273 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7274 /*
7275  * Based on:
7276  * Ext JS Library 1.1.1
7277  * Copyright(c) 2006-2007, Ext JS, LLC.
7278  *
7279  * Originally Released Under LGPL - original licence link has changed is not relivant.
7280  *
7281  * Fork - LGPL
7282  * <script type="text/javascript">
7283  */
7284  
7285 /**
7286  * @class Roo.LoadMask
7287  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7288  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7289  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7290  * element's UpdateManager load indicator and will be destroyed after the initial load.
7291  * @constructor
7292  * Create a new LoadMask
7293  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7294  * @param {Object} config The config object
7295  */
7296 Roo.LoadMask = function(el, config){
7297     this.el = Roo.get(el);
7298     Roo.apply(this, config);
7299     if(this.store){
7300         this.store.on('beforeload', this.onBeforeLoad, this);
7301         this.store.on('load', this.onLoad, this);
7302         this.store.on('loadexception', this.onLoadException, this);
7303         this.removeMask = false;
7304     }else{
7305         var um = this.el.getUpdateManager();
7306         um.showLoadIndicator = false; // disable the default indicator
7307         um.on('beforeupdate', this.onBeforeLoad, this);
7308         um.on('update', this.onLoad, this);
7309         um.on('failure', this.onLoad, this);
7310         this.removeMask = true;
7311     }
7312 };
7313
7314 Roo.LoadMask.prototype = {
7315     /**
7316      * @cfg {Boolean} removeMask
7317      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7318      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7319      */
7320     /**
7321      * @cfg {String} msg
7322      * The text to display in a centered loading message box (defaults to 'Loading...')
7323      */
7324     msg : 'Loading...',
7325     /**
7326      * @cfg {String} msgCls
7327      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7328      */
7329     msgCls : 'x-mask-loading',
7330
7331     /**
7332      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7333      * @type Boolean
7334      */
7335     disabled: false,
7336
7337     /**
7338      * Disables the mask to prevent it from being displayed
7339      */
7340     disable : function(){
7341        this.disabled = true;
7342     },
7343
7344     /**
7345      * Enables the mask so that it can be displayed
7346      */
7347     enable : function(){
7348         this.disabled = false;
7349     },
7350     
7351     onLoadException : function()
7352     {
7353         Roo.log(arguments);
7354         
7355         if (typeof(arguments[3]) != 'undefined') {
7356             Roo.MessageBox.alert("Error loading",arguments[3]);
7357         } 
7358         /*
7359         try {
7360             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7361                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7362             }   
7363         } catch(e) {
7364             
7365         }
7366         */
7367     
7368         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7369     },
7370     // private
7371     onLoad : function()
7372     {
7373         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7374     },
7375
7376     // private
7377     onBeforeLoad : function(){
7378         if(!this.disabled){
7379             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7380         }
7381     },
7382
7383     // private
7384     destroy : function(){
7385         if(this.store){
7386             this.store.un('beforeload', this.onBeforeLoad, this);
7387             this.store.un('load', this.onLoad, this);
7388             this.store.un('loadexception', this.onLoadException, this);
7389         }else{
7390             var um = this.el.getUpdateManager();
7391             um.un('beforeupdate', this.onBeforeLoad, this);
7392             um.un('update', this.onLoad, this);
7393             um.un('failure', this.onLoad, this);
7394         }
7395     }
7396 };/*
7397  * - LGPL
7398  *
7399  * table
7400  * 
7401  */
7402
7403 /**
7404  * @class Roo.bootstrap.Table
7405  * @extends Roo.bootstrap.Component
7406  * Bootstrap Table class
7407  * @cfg {String} cls table class
7408  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7409  * @cfg {String} bgcolor Specifies the background color for a table
7410  * @cfg {Number} border Specifies whether the table cells should have borders or not
7411  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7412  * @cfg {Number} cellspacing Specifies the space between cells
7413  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7414  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7415  * @cfg {String} sortable Specifies that the table should be sortable
7416  * @cfg {String} summary Specifies a summary of the content of a table
7417  * @cfg {Number} width Specifies the width of a table
7418  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7419  * 
7420  * @cfg {boolean} striped Should the rows be alternative striped
7421  * @cfg {boolean} bordered Add borders to the table
7422  * @cfg {boolean} hover Add hover highlighting
7423  * @cfg {boolean} condensed Format condensed
7424  * @cfg {boolean} responsive Format condensed
7425  * @cfg {Boolean} loadMask (true|false) default false
7426  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7427  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7428  * @cfg {Boolean} rowSelection (true|false) default false
7429  * @cfg {Boolean} cellSelection (true|false) default false
7430  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7431  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7432  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7433  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7434  
7435  * 
7436  * @constructor
7437  * Create a new Table
7438  * @param {Object} config The config object
7439  */
7440
7441 Roo.bootstrap.Table = function(config){
7442     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7443     
7444   
7445     
7446     // BC...
7447     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7448     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7449     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7450     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7451     
7452     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7453     if (this.sm) {
7454         this.sm.grid = this;
7455         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7456         this.sm = this.selModel;
7457         this.sm.xmodule = this.xmodule || false;
7458     }
7459     
7460     if (this.cm && typeof(this.cm.config) == 'undefined') {
7461         this.colModel = new Roo.grid.ColumnModel(this.cm);
7462         this.cm = this.colModel;
7463         this.cm.xmodule = this.xmodule || false;
7464     }
7465     if (this.store) {
7466         this.store= Roo.factory(this.store, Roo.data);
7467         this.ds = this.store;
7468         this.ds.xmodule = this.xmodule || false;
7469          
7470     }
7471     if (this.footer && this.store) {
7472         this.footer.dataSource = this.ds;
7473         this.footer = Roo.factory(this.footer);
7474     }
7475     
7476     /** @private */
7477     this.addEvents({
7478         /**
7479          * @event cellclick
7480          * Fires when a cell is clicked
7481          * @param {Roo.bootstrap.Table} this
7482          * @param {Roo.Element} el
7483          * @param {Number} rowIndex
7484          * @param {Number} columnIndex
7485          * @param {Roo.EventObject} e
7486          */
7487         "cellclick" : true,
7488         /**
7489          * @event celldblclick
7490          * Fires when a cell is double clicked
7491          * @param {Roo.bootstrap.Table} this
7492          * @param {Roo.Element} el
7493          * @param {Number} rowIndex
7494          * @param {Number} columnIndex
7495          * @param {Roo.EventObject} e
7496          */
7497         "celldblclick" : true,
7498         /**
7499          * @event rowclick
7500          * Fires when a row is clicked
7501          * @param {Roo.bootstrap.Table} this
7502          * @param {Roo.Element} el
7503          * @param {Number} rowIndex
7504          * @param {Roo.EventObject} e
7505          */
7506         "rowclick" : true,
7507         /**
7508          * @event rowdblclick
7509          * Fires when a row is double clicked
7510          * @param {Roo.bootstrap.Table} this
7511          * @param {Roo.Element} el
7512          * @param {Number} rowIndex
7513          * @param {Roo.EventObject} e
7514          */
7515         "rowdblclick" : true,
7516         /**
7517          * @event mouseover
7518          * Fires when a mouseover occur
7519          * @param {Roo.bootstrap.Table} this
7520          * @param {Roo.Element} el
7521          * @param {Number} rowIndex
7522          * @param {Number} columnIndex
7523          * @param {Roo.EventObject} e
7524          */
7525         "mouseover" : true,
7526         /**
7527          * @event mouseout
7528          * Fires when a mouseout occur
7529          * @param {Roo.bootstrap.Table} this
7530          * @param {Roo.Element} el
7531          * @param {Number} rowIndex
7532          * @param {Number} columnIndex
7533          * @param {Roo.EventObject} e
7534          */
7535         "mouseout" : true,
7536         /**
7537          * @event rowclass
7538          * Fires when a row is rendered, so you can change add a style to it.
7539          * @param {Roo.bootstrap.Table} this
7540          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7541          */
7542         'rowclass' : true,
7543           /**
7544          * @event rowsrendered
7545          * Fires when all the  rows have been rendered
7546          * @param {Roo.bootstrap.Table} this
7547          */
7548         'rowsrendered' : true,
7549         /**
7550          * @event contextmenu
7551          * The raw contextmenu event for the entire grid.
7552          * @param {Roo.EventObject} e
7553          */
7554         "contextmenu" : true,
7555         /**
7556          * @event rowcontextmenu
7557          * Fires when a row is right clicked
7558          * @param {Roo.bootstrap.Table} this
7559          * @param {Number} rowIndex
7560          * @param {Roo.EventObject} e
7561          */
7562         "rowcontextmenu" : true,
7563         /**
7564          * @event cellcontextmenu
7565          * Fires when a cell is right clicked
7566          * @param {Roo.bootstrap.Table} this
7567          * @param {Number} rowIndex
7568          * @param {Number} cellIndex
7569          * @param {Roo.EventObject} e
7570          */
7571          "cellcontextmenu" : true,
7572          /**
7573          * @event headercontextmenu
7574          * Fires when a header is right clicked
7575          * @param {Roo.bootstrap.Table} this
7576          * @param {Number} columnIndex
7577          * @param {Roo.EventObject} e
7578          */
7579         "headercontextmenu" : true
7580     });
7581 };
7582
7583 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7584     
7585     cls: false,
7586     align: false,
7587     bgcolor: false,
7588     border: false,
7589     cellpadding: false,
7590     cellspacing: false,
7591     frame: false,
7592     rules: false,
7593     sortable: false,
7594     summary: false,
7595     width: false,
7596     striped : false,
7597     scrollBody : false,
7598     bordered: false,
7599     hover:  false,
7600     condensed : false,
7601     responsive : false,
7602     sm : false,
7603     cm : false,
7604     store : false,
7605     loadMask : false,
7606     footerShow : true,
7607     headerShow : true,
7608   
7609     rowSelection : false,
7610     cellSelection : false,
7611     layout : false,
7612     
7613     // Roo.Element - the tbody
7614     mainBody: false,
7615     // Roo.Element - thead element
7616     mainHead: false,
7617     
7618     container: false, // used by gridpanel...
7619     
7620     lazyLoad : false,
7621     
7622     CSS : Roo.util.CSS,
7623     
7624     auto_hide_footer : false,
7625     
7626     getAutoCreate : function()
7627     {
7628         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7629         
7630         cfg = {
7631             tag: 'table',
7632             cls : 'table',
7633             cn : []
7634         };
7635         if (this.scrollBody) {
7636             cfg.cls += ' table-body-fixed';
7637         }    
7638         if (this.striped) {
7639             cfg.cls += ' table-striped';
7640         }
7641         
7642         if (this.hover) {
7643             cfg.cls += ' table-hover';
7644         }
7645         if (this.bordered) {
7646             cfg.cls += ' table-bordered';
7647         }
7648         if (this.condensed) {
7649             cfg.cls += ' table-condensed';
7650         }
7651         if (this.responsive) {
7652             cfg.cls += ' table-responsive';
7653         }
7654         
7655         if (this.cls) {
7656             cfg.cls+=  ' ' +this.cls;
7657         }
7658         
7659         // this lot should be simplifed...
7660         var _t = this;
7661         var cp = [
7662             'align',
7663             'bgcolor',
7664             'border',
7665             'cellpadding',
7666             'cellspacing',
7667             'frame',
7668             'rules',
7669             'sortable',
7670             'summary',
7671             'width'
7672         ].forEach(function(k) {
7673             if (_t[k]) {
7674                 cfg[k] = _t[k];
7675             }
7676         });
7677         
7678         
7679         if (this.layout) {
7680             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7681         }
7682         
7683         if(this.store || this.cm){
7684             if(this.headerShow){
7685                 cfg.cn.push(this.renderHeader());
7686             }
7687             
7688             cfg.cn.push(this.renderBody());
7689             
7690             if(this.footerShow){
7691                 cfg.cn.push(this.renderFooter());
7692             }
7693             // where does this come from?
7694             //cfg.cls+=  ' TableGrid';
7695         }
7696         
7697         return { cn : [ cfg ] };
7698     },
7699     
7700     initEvents : function()
7701     {   
7702         if(!this.store || !this.cm){
7703             return;
7704         }
7705         if (this.selModel) {
7706             this.selModel.initEvents();
7707         }
7708         
7709         
7710         //Roo.log('initEvents with ds!!!!');
7711         
7712         this.mainBody = this.el.select('tbody', true).first();
7713         this.mainHead = this.el.select('thead', true).first();
7714         this.mainFoot = this.el.select('tfoot', true).first();
7715         
7716         
7717         
7718         var _this = this;
7719         
7720         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7721             e.on('click', _this.sort, _this);
7722         });
7723         
7724         this.mainBody.on("click", this.onClick, this);
7725         this.mainBody.on("dblclick", this.onDblClick, this);
7726         
7727         // why is this done????? = it breaks dialogs??
7728         //this.parent().el.setStyle('position', 'relative');
7729         
7730         
7731         if (this.footer) {
7732             this.footer.parentId = this.id;
7733             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7734             
7735             if(this.lazyLoad){
7736                 this.el.select('tfoot tr td').first().addClass('hide');
7737             }
7738         } 
7739         
7740         if(this.loadMask) {
7741             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7742         }
7743         
7744         this.store.on('load', this.onLoad, this);
7745         this.store.on('beforeload', this.onBeforeLoad, this);
7746         this.store.on('update', this.onUpdate, this);
7747         this.store.on('add', this.onAdd, this);
7748         this.store.on("clear", this.clear, this);
7749         
7750         this.el.on("contextmenu", this.onContextMenu, this);
7751         
7752         this.mainBody.on('scroll', this.onBodyScroll, this);
7753         
7754         this.cm.on("headerchange", this.onHeaderChange, this);
7755         
7756         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7757         
7758     },
7759     
7760     onContextMenu : function(e, t)
7761     {
7762         this.processEvent("contextmenu", e);
7763     },
7764     
7765     processEvent : function(name, e)
7766     {
7767         if (name != 'touchstart' ) {
7768             this.fireEvent(name, e);    
7769         }
7770         
7771         var t = e.getTarget();
7772         
7773         var cell = Roo.get(t);
7774         
7775         if(!cell){
7776             return;
7777         }
7778         
7779         if(cell.findParent('tfoot', false, true)){
7780             return;
7781         }
7782         
7783         if(cell.findParent('thead', false, true)){
7784             
7785             if(e.getTarget().nodeName.toLowerCase() != 'th'){
7786                 cell = Roo.get(t).findParent('th', false, true);
7787                 if (!cell) {
7788                     Roo.log("failed to find th in thead?");
7789                     Roo.log(e.getTarget());
7790                     return;
7791                 }
7792             }
7793             
7794             var cellIndex = cell.dom.cellIndex;
7795             
7796             var ename = name == 'touchstart' ? 'click' : name;
7797             this.fireEvent("header" + ename, this, cellIndex, e);
7798             
7799             return;
7800         }
7801         
7802         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7803             cell = Roo.get(t).findParent('td', false, true);
7804             if (!cell) {
7805                 Roo.log("failed to find th in tbody?");
7806                 Roo.log(e.getTarget());
7807                 return;
7808             }
7809         }
7810         
7811         var row = cell.findParent('tr', false, true);
7812         var cellIndex = cell.dom.cellIndex;
7813         var rowIndex = row.dom.rowIndex - 1;
7814         
7815         if(row !== false){
7816             
7817             this.fireEvent("row" + name, this, rowIndex, e);
7818             
7819             if(cell !== false){
7820             
7821                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7822             }
7823         }
7824         
7825     },
7826     
7827     onMouseover : function(e, el)
7828     {
7829         var cell = Roo.get(el);
7830         
7831         if(!cell){
7832             return;
7833         }
7834         
7835         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7836             cell = cell.findParent('td', false, true);
7837         }
7838         
7839         var row = cell.findParent('tr', false, true);
7840         var cellIndex = cell.dom.cellIndex;
7841         var rowIndex = row.dom.rowIndex - 1; // start from 0
7842         
7843         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7844         
7845     },
7846     
7847     onMouseout : function(e, el)
7848     {
7849         var cell = Roo.get(el);
7850         
7851         if(!cell){
7852             return;
7853         }
7854         
7855         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7856             cell = cell.findParent('td', false, true);
7857         }
7858         
7859         var row = cell.findParent('tr', false, true);
7860         var cellIndex = cell.dom.cellIndex;
7861         var rowIndex = row.dom.rowIndex - 1; // start from 0
7862         
7863         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7864         
7865     },
7866     
7867     onClick : function(e, el)
7868     {
7869         var cell = Roo.get(el);
7870         
7871         if(!cell || (!this.cellSelection && !this.rowSelection)){
7872             return;
7873         }
7874         
7875         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7876             cell = cell.findParent('td', false, true);
7877         }
7878         
7879         if(!cell || typeof(cell) == 'undefined'){
7880             return;
7881         }
7882         
7883         var row = cell.findParent('tr', false, true);
7884         
7885         if(!row || typeof(row) == 'undefined'){
7886             return;
7887         }
7888         
7889         var cellIndex = cell.dom.cellIndex;
7890         var rowIndex = this.getRowIndex(row);
7891         
7892         // why??? - should these not be based on SelectionModel?
7893         if(this.cellSelection){
7894             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7895         }
7896         
7897         if(this.rowSelection){
7898             this.fireEvent('rowclick', this, row, rowIndex, e);
7899         }
7900         
7901         
7902     },
7903         
7904     onDblClick : function(e,el)
7905     {
7906         var cell = Roo.get(el);
7907         
7908         if(!cell || (!this.cellSelection && !this.rowSelection)){
7909             return;
7910         }
7911         
7912         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7913             cell = cell.findParent('td', false, true);
7914         }
7915         
7916         if(!cell || typeof(cell) == 'undefined'){
7917             return;
7918         }
7919         
7920         var row = cell.findParent('tr', false, true);
7921         
7922         if(!row || typeof(row) == 'undefined'){
7923             return;
7924         }
7925         
7926         var cellIndex = cell.dom.cellIndex;
7927         var rowIndex = this.getRowIndex(row);
7928         
7929         if(this.cellSelection){
7930             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7931         }
7932         
7933         if(this.rowSelection){
7934             this.fireEvent('rowdblclick', this, row, rowIndex, e);
7935         }
7936     },
7937     
7938     sort : function(e,el)
7939     {
7940         var col = Roo.get(el);
7941         
7942         if(!col.hasClass('sortable')){
7943             return;
7944         }
7945         
7946         var sort = col.attr('sort');
7947         var dir = 'ASC';
7948         
7949         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7950             dir = 'DESC';
7951         }
7952         
7953         this.store.sortInfo = {field : sort, direction : dir};
7954         
7955         if (this.footer) {
7956             Roo.log("calling footer first");
7957             this.footer.onClick('first');
7958         } else {
7959         
7960             this.store.load({ params : { start : 0 } });
7961         }
7962     },
7963     
7964     renderHeader : function()
7965     {
7966         var header = {
7967             tag: 'thead',
7968             cn : []
7969         };
7970         
7971         var cm = this.cm;
7972         this.totalWidth = 0;
7973         
7974         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7975             
7976             var config = cm.config[i];
7977             
7978             var c = {
7979                 tag: 'th',
7980                 cls : 'x-hcol-' + i,
7981                 style : '',
7982                 html: cm.getColumnHeader(i)
7983             };
7984             
7985             var hh = '';
7986             
7987             if(typeof(config.sortable) != 'undefined' && config.sortable){
7988                 c.cls = 'sortable';
7989                 c.html = '<i class="glyphicon"></i>' + c.html;
7990             }
7991             
7992             // could use BS4 hidden-..-down 
7993             
7994             if(typeof(config.lgHeader) != 'undefined'){
7995                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
7996             }
7997             
7998             if(typeof(config.mdHeader) != 'undefined'){
7999                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8000             }
8001             
8002             if(typeof(config.smHeader) != 'undefined'){
8003                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8004             }
8005             
8006             if(typeof(config.xsHeader) != 'undefined'){
8007                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8008             }
8009             
8010             if(hh.length){
8011                 c.html = hh;
8012             }
8013             
8014             if(typeof(config.tooltip) != 'undefined'){
8015                 c.tooltip = config.tooltip;
8016             }
8017             
8018             if(typeof(config.colspan) != 'undefined'){
8019                 c.colspan = config.colspan;
8020             }
8021             
8022             if(typeof(config.hidden) != 'undefined' && config.hidden){
8023                 c.style += ' display:none;';
8024             }
8025             
8026             if(typeof(config.dataIndex) != 'undefined'){
8027                 c.sort = config.dataIndex;
8028             }
8029             
8030            
8031             
8032             if(typeof(config.align) != 'undefined' && config.align.length){
8033                 c.style += ' text-align:' + config.align + ';';
8034             }
8035             
8036             if(typeof(config.width) != 'undefined'){
8037                 c.style += ' width:' + config.width + 'px;';
8038                 this.totalWidth += config.width;
8039             } else {
8040                 this.totalWidth += 100; // assume minimum of 100 per column?
8041             }
8042             
8043             if(typeof(config.cls) != 'undefined'){
8044                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8045             }
8046             
8047             ['xs','sm','md','lg'].map(function(size){
8048                 
8049                 if(typeof(config[size]) == 'undefined'){
8050                     return;
8051                 }
8052                  
8053                 if (!config[size]) { // 0 = hidden
8054                     // BS 4 '0' is treated as hide that column and below.
8055                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8056                     return;
8057                 }
8058                 
8059                 c.cls += ' col-' + size + '-' + config[size] + (
8060                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8061                 );
8062                 
8063                 
8064             });
8065             
8066             header.cn.push(c)
8067         }
8068         
8069         return header;
8070     },
8071     
8072     renderBody : function()
8073     {
8074         var body = {
8075             tag: 'tbody',
8076             cn : [
8077                 {
8078                     tag: 'tr',
8079                     cn : [
8080                         {
8081                             tag : 'td',
8082                             colspan :  this.cm.getColumnCount()
8083                         }
8084                     ]
8085                 }
8086             ]
8087         };
8088         
8089         return body;
8090     },
8091     
8092     renderFooter : function()
8093     {
8094         var footer = {
8095             tag: 'tfoot',
8096             cn : [
8097                 {
8098                     tag: 'tr',
8099                     cn : [
8100                         {
8101                             tag : 'td',
8102                             colspan :  this.cm.getColumnCount()
8103                         }
8104                     ]
8105                 }
8106             ]
8107         };
8108         
8109         return footer;
8110     },
8111     
8112     
8113     
8114     onLoad : function()
8115     {
8116 //        Roo.log('ds onload');
8117         this.clear();
8118         
8119         var _this = this;
8120         var cm = this.cm;
8121         var ds = this.store;
8122         
8123         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8124             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8125             if (_this.store.sortInfo) {
8126                     
8127                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8128                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8129                 }
8130                 
8131                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8132                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8133                 }
8134             }
8135         });
8136         
8137         var tbody =  this.mainBody;
8138               
8139         if(ds.getCount() > 0){
8140             ds.data.each(function(d,rowIndex){
8141                 var row =  this.renderRow(cm, ds, rowIndex);
8142                 
8143                 tbody.createChild(row);
8144                 
8145                 var _this = this;
8146                 
8147                 if(row.cellObjects.length){
8148                     Roo.each(row.cellObjects, function(r){
8149                         _this.renderCellObject(r);
8150                     })
8151                 }
8152                 
8153             }, this);
8154         }
8155         
8156         var tfoot = this.el.select('tfoot', true).first();
8157         
8158         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8159             
8160             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8161             
8162             var total = this.ds.getTotalCount();
8163             
8164             if(this.footer.pageSize < total){
8165                 this.mainFoot.show();
8166             }
8167         }
8168         
8169         Roo.each(this.el.select('tbody td', true).elements, function(e){
8170             e.on('mouseover', _this.onMouseover, _this);
8171         });
8172         
8173         Roo.each(this.el.select('tbody td', true).elements, function(e){
8174             e.on('mouseout', _this.onMouseout, _this);
8175         });
8176         this.fireEvent('rowsrendered', this);
8177         
8178         this.autoSize();
8179     },
8180     
8181     
8182     onUpdate : function(ds,record)
8183     {
8184         this.refreshRow(record);
8185         this.autoSize();
8186     },
8187     
8188     onRemove : function(ds, record, index, isUpdate){
8189         if(isUpdate !== true){
8190             this.fireEvent("beforerowremoved", this, index, record);
8191         }
8192         var bt = this.mainBody.dom;
8193         
8194         var rows = this.el.select('tbody > tr', true).elements;
8195         
8196         if(typeof(rows[index]) != 'undefined'){
8197             bt.removeChild(rows[index].dom);
8198         }
8199         
8200 //        if(bt.rows[index]){
8201 //            bt.removeChild(bt.rows[index]);
8202 //        }
8203         
8204         if(isUpdate !== true){
8205             //this.stripeRows(index);
8206             //this.syncRowHeights(index, index);
8207             //this.layout();
8208             this.fireEvent("rowremoved", this, index, record);
8209         }
8210     },
8211     
8212     onAdd : function(ds, records, rowIndex)
8213     {
8214         //Roo.log('on Add called');
8215         // - note this does not handle multiple adding very well..
8216         var bt = this.mainBody.dom;
8217         for (var i =0 ; i < records.length;i++) {
8218             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8219             //Roo.log(records[i]);
8220             //Roo.log(this.store.getAt(rowIndex+i));
8221             this.insertRow(this.store, rowIndex + i, false);
8222             return;
8223         }
8224         
8225     },
8226     
8227     
8228     refreshRow : function(record){
8229         var ds = this.store, index;
8230         if(typeof record == 'number'){
8231             index = record;
8232             record = ds.getAt(index);
8233         }else{
8234             index = ds.indexOf(record);
8235         }
8236         this.insertRow(ds, index, true);
8237         this.autoSize();
8238         this.onRemove(ds, record, index+1, true);
8239         this.autoSize();
8240         //this.syncRowHeights(index, index);
8241         //this.layout();
8242         this.fireEvent("rowupdated", this, index, record);
8243     },
8244     
8245     insertRow : function(dm, rowIndex, isUpdate){
8246         
8247         if(!isUpdate){
8248             this.fireEvent("beforerowsinserted", this, rowIndex);
8249         }
8250             //var s = this.getScrollState();
8251         var row = this.renderRow(this.cm, this.store, rowIndex);
8252         // insert before rowIndex..
8253         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8254         
8255         var _this = this;
8256                 
8257         if(row.cellObjects.length){
8258             Roo.each(row.cellObjects, function(r){
8259                 _this.renderCellObject(r);
8260             })
8261         }
8262             
8263         if(!isUpdate){
8264             this.fireEvent("rowsinserted", this, rowIndex);
8265             //this.syncRowHeights(firstRow, lastRow);
8266             //this.stripeRows(firstRow);
8267             //this.layout();
8268         }
8269         
8270     },
8271     
8272     
8273     getRowDom : function(rowIndex)
8274     {
8275         var rows = this.el.select('tbody > tr', true).elements;
8276         
8277         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8278         
8279     },
8280     // returns the object tree for a tr..
8281   
8282     
8283     renderRow : function(cm, ds, rowIndex) 
8284     {
8285         var d = ds.getAt(rowIndex);
8286         
8287         var row = {
8288             tag : 'tr',
8289             cls : 'x-row-' + rowIndex,
8290             cn : []
8291         };
8292             
8293         var cellObjects = [];
8294         
8295         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8296             var config = cm.config[i];
8297             
8298             var renderer = cm.getRenderer(i);
8299             var value = '';
8300             var id = false;
8301             
8302             if(typeof(renderer) !== 'undefined'){
8303                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8304             }
8305             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8306             // and are rendered into the cells after the row is rendered - using the id for the element.
8307             
8308             if(typeof(value) === 'object'){
8309                 id = Roo.id();
8310                 cellObjects.push({
8311                     container : id,
8312                     cfg : value 
8313                 })
8314             }
8315             
8316             var rowcfg = {
8317                 record: d,
8318                 rowIndex : rowIndex,
8319                 colIndex : i,
8320                 rowClass : ''
8321             };
8322
8323             this.fireEvent('rowclass', this, rowcfg);
8324             
8325             var td = {
8326                 tag: 'td',
8327                 cls : rowcfg.rowClass + ' x-col-' + i,
8328                 style: '',
8329                 html: (typeof(value) === 'object') ? '' : value
8330             };
8331             
8332             if (id) {
8333                 td.id = id;
8334             }
8335             
8336             if(typeof(config.colspan) != 'undefined'){
8337                 td.colspan = config.colspan;
8338             }
8339             
8340             if(typeof(config.hidden) != 'undefined' && config.hidden){
8341                 td.style += ' display:none;';
8342             }
8343             
8344             if(typeof(config.align) != 'undefined' && config.align.length){
8345                 td.style += ' text-align:' + config.align + ';';
8346             }
8347             if(typeof(config.valign) != 'undefined' && config.valign.length){
8348                 td.style += ' vertical-align:' + config.valign + ';';
8349             }
8350             
8351             if(typeof(config.width) != 'undefined'){
8352                 td.style += ' width:' +  config.width + 'px;';
8353             }
8354             
8355             if(typeof(config.cursor) != 'undefined'){
8356                 td.style += ' cursor:' +  config.cursor + ';';
8357             }
8358             
8359             if(typeof(config.cls) != 'undefined'){
8360                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8361             }
8362             
8363             ['xs','sm','md','lg'].map(function(size){
8364                 
8365                 if(typeof(config[size]) == 'undefined'){
8366                     return;
8367                 }
8368                 
8369                 
8370                   
8371                 if (!config[size]) { // 0 = hidden
8372                     // BS 4 '0' is treated as hide that column and below.
8373                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8374                     return;
8375                 }
8376                 
8377                 td.cls += ' col-' + size + '-' + config[size] + (
8378                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8379                 );
8380                  
8381
8382             });
8383             
8384             row.cn.push(td);
8385            
8386         }
8387         
8388         row.cellObjects = cellObjects;
8389         
8390         return row;
8391           
8392     },
8393     
8394     
8395     
8396     onBeforeLoad : function()
8397     {
8398         
8399     },
8400      /**
8401      * Remove all rows
8402      */
8403     clear : function()
8404     {
8405         this.el.select('tbody', true).first().dom.innerHTML = '';
8406     },
8407     /**
8408      * Show or hide a row.
8409      * @param {Number} rowIndex to show or hide
8410      * @param {Boolean} state hide
8411      */
8412     setRowVisibility : function(rowIndex, state)
8413     {
8414         var bt = this.mainBody.dom;
8415         
8416         var rows = this.el.select('tbody > tr', true).elements;
8417         
8418         if(typeof(rows[rowIndex]) == 'undefined'){
8419             return;
8420         }
8421         rows[rowIndex].dom.style.display = state ? '' : 'none';
8422     },
8423     
8424     
8425     getSelectionModel : function(){
8426         if(!this.selModel){
8427             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8428         }
8429         return this.selModel;
8430     },
8431     /*
8432      * Render the Roo.bootstrap object from renderder
8433      */
8434     renderCellObject : function(r)
8435     {
8436         var _this = this;
8437         
8438         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8439         
8440         var t = r.cfg.render(r.container);
8441         
8442         if(r.cfg.cn){
8443             Roo.each(r.cfg.cn, function(c){
8444                 var child = {
8445                     container: t.getChildContainer(),
8446                     cfg: c
8447                 };
8448                 _this.renderCellObject(child);
8449             })
8450         }
8451     },
8452     
8453     getRowIndex : function(row)
8454     {
8455         var rowIndex = -1;
8456         
8457         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8458             if(el != row){
8459                 return;
8460             }
8461             
8462             rowIndex = index;
8463         });
8464         
8465         return rowIndex;
8466     },
8467      /**
8468      * Returns the grid's underlying element = used by panel.Grid
8469      * @return {Element} The element
8470      */
8471     getGridEl : function(){
8472         return this.el;
8473     },
8474      /**
8475      * Forces a resize - used by panel.Grid
8476      * @return {Element} The element
8477      */
8478     autoSize : function()
8479     {
8480         //var ctr = Roo.get(this.container.dom.parentElement);
8481         var ctr = Roo.get(this.el.dom);
8482         
8483         var thd = this.getGridEl().select('thead',true).first();
8484         var tbd = this.getGridEl().select('tbody', true).first();
8485         var tfd = this.getGridEl().select('tfoot', true).first();
8486         
8487         var cw = ctr.getWidth();
8488         
8489         if (tbd) {
8490             
8491             tbd.setWidth(ctr.getWidth());
8492             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8493             // this needs fixing for various usage - currently only hydra job advers I think..
8494             //tdb.setHeight(
8495             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8496             //); 
8497             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8498             cw -= barsize;
8499         }
8500         cw = Math.max(cw, this.totalWidth);
8501         this.getGridEl().select('tr',true).setWidth(cw);
8502         // resize 'expandable coloumn?
8503         
8504         return; // we doe not have a view in this design..
8505         
8506     },
8507     onBodyScroll: function()
8508     {
8509         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8510         if(this.mainHead){
8511             this.mainHead.setStyle({
8512                 'position' : 'relative',
8513                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8514             });
8515         }
8516         
8517         if(this.lazyLoad){
8518             
8519             var scrollHeight = this.mainBody.dom.scrollHeight;
8520             
8521             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8522             
8523             var height = this.mainBody.getHeight();
8524             
8525             if(scrollHeight - height == scrollTop) {
8526                 
8527                 var total = this.ds.getTotalCount();
8528                 
8529                 if(this.footer.cursor + this.footer.pageSize < total){
8530                     
8531                     this.footer.ds.load({
8532                         params : {
8533                             start : this.footer.cursor + this.footer.pageSize,
8534                             limit : this.footer.pageSize
8535                         },
8536                         add : true
8537                     });
8538                 }
8539             }
8540             
8541         }
8542     },
8543     
8544     onHeaderChange : function()
8545     {
8546         var header = this.renderHeader();
8547         var table = this.el.select('table', true).first();
8548         
8549         this.mainHead.remove();
8550         this.mainHead = table.createChild(header, this.mainBody, false);
8551     },
8552     
8553     onHiddenChange : function(colModel, colIndex, hidden)
8554     {
8555         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8556         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8557         
8558         this.CSS.updateRule(thSelector, "display", "");
8559         this.CSS.updateRule(tdSelector, "display", "");
8560         
8561         if(hidden){
8562             this.CSS.updateRule(thSelector, "display", "none");
8563             this.CSS.updateRule(tdSelector, "display", "none");
8564         }
8565         
8566         this.onHeaderChange();
8567         this.onLoad();
8568     },
8569     
8570     setColumnWidth: function(col_index, width)
8571     {
8572         // width = "md-2 xs-2..."
8573         if(!this.colModel.config[col_index]) {
8574             return;
8575         }
8576         
8577         var w = width.split(" ");
8578         
8579         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8580         
8581         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8582         
8583         
8584         for(var j = 0; j < w.length; j++) {
8585             
8586             if(!w[j]) {
8587                 continue;
8588             }
8589             
8590             var size_cls = w[j].split("-");
8591             
8592             if(!Number.isInteger(size_cls[1] * 1)) {
8593                 continue;
8594             }
8595             
8596             if(!this.colModel.config[col_index][size_cls[0]]) {
8597                 continue;
8598             }
8599             
8600             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8601                 continue;
8602             }
8603             
8604             h_row[0].classList.replace(
8605                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8606                 "col-"+size_cls[0]+"-"+size_cls[1]
8607             );
8608             
8609             for(var i = 0; i < rows.length; i++) {
8610                 
8611                 var size_cls = w[j].split("-");
8612                 
8613                 if(!Number.isInteger(size_cls[1] * 1)) {
8614                     continue;
8615                 }
8616                 
8617                 if(!this.colModel.config[col_index][size_cls[0]]) {
8618                     continue;
8619                 }
8620                 
8621                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8622                     continue;
8623                 }
8624                 
8625                 rows[i].classList.replace(
8626                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8627                     "col-"+size_cls[0]+"-"+size_cls[1]
8628                 );
8629             }
8630             
8631             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8632         }
8633     }
8634 });
8635
8636  
8637
8638  /*
8639  * - LGPL
8640  *
8641  * table cell
8642  * 
8643  */
8644
8645 /**
8646  * @class Roo.bootstrap.TableCell
8647  * @extends Roo.bootstrap.Component
8648  * Bootstrap TableCell class
8649  * @cfg {String} html cell contain text
8650  * @cfg {String} cls cell class
8651  * @cfg {String} tag cell tag (td|th) default td
8652  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8653  * @cfg {String} align Aligns the content in a cell
8654  * @cfg {String} axis Categorizes cells
8655  * @cfg {String} bgcolor Specifies the background color of a cell
8656  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8657  * @cfg {Number} colspan Specifies the number of columns a cell should span
8658  * @cfg {String} headers Specifies one or more header cells a cell is related to
8659  * @cfg {Number} height Sets the height of a cell
8660  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8661  * @cfg {Number} rowspan Sets the number of rows a cell should span
8662  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8663  * @cfg {String} valign Vertical aligns the content in a cell
8664  * @cfg {Number} width Specifies the width of a cell
8665  * 
8666  * @constructor
8667  * Create a new TableCell
8668  * @param {Object} config The config object
8669  */
8670
8671 Roo.bootstrap.TableCell = function(config){
8672     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8673 };
8674
8675 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8676     
8677     html: false,
8678     cls: false,
8679     tag: false,
8680     abbr: false,
8681     align: false,
8682     axis: false,
8683     bgcolor: false,
8684     charoff: false,
8685     colspan: false,
8686     headers: false,
8687     height: false,
8688     nowrap: false,
8689     rowspan: false,
8690     scope: false,
8691     valign: false,
8692     width: false,
8693     
8694     
8695     getAutoCreate : function(){
8696         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8697         
8698         cfg = {
8699             tag: 'td'
8700         };
8701         
8702         if(this.tag){
8703             cfg.tag = this.tag;
8704         }
8705         
8706         if (this.html) {
8707             cfg.html=this.html
8708         }
8709         if (this.cls) {
8710             cfg.cls=this.cls
8711         }
8712         if (this.abbr) {
8713             cfg.abbr=this.abbr
8714         }
8715         if (this.align) {
8716             cfg.align=this.align
8717         }
8718         if (this.axis) {
8719             cfg.axis=this.axis
8720         }
8721         if (this.bgcolor) {
8722             cfg.bgcolor=this.bgcolor
8723         }
8724         if (this.charoff) {
8725             cfg.charoff=this.charoff
8726         }
8727         if (this.colspan) {
8728             cfg.colspan=this.colspan
8729         }
8730         if (this.headers) {
8731             cfg.headers=this.headers
8732         }
8733         if (this.height) {
8734             cfg.height=this.height
8735         }
8736         if (this.nowrap) {
8737             cfg.nowrap=this.nowrap
8738         }
8739         if (this.rowspan) {
8740             cfg.rowspan=this.rowspan
8741         }
8742         if (this.scope) {
8743             cfg.scope=this.scope
8744         }
8745         if (this.valign) {
8746             cfg.valign=this.valign
8747         }
8748         if (this.width) {
8749             cfg.width=this.width
8750         }
8751         
8752         
8753         return cfg;
8754     }
8755    
8756 });
8757
8758  
8759
8760  /*
8761  * - LGPL
8762  *
8763  * table row
8764  * 
8765  */
8766
8767 /**
8768  * @class Roo.bootstrap.TableRow
8769  * @extends Roo.bootstrap.Component
8770  * Bootstrap TableRow class
8771  * @cfg {String} cls row class
8772  * @cfg {String} align Aligns the content in a table row
8773  * @cfg {String} bgcolor Specifies a background color for a table row
8774  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8775  * @cfg {String} valign Vertical aligns the content in a table row
8776  * 
8777  * @constructor
8778  * Create a new TableRow
8779  * @param {Object} config The config object
8780  */
8781
8782 Roo.bootstrap.TableRow = function(config){
8783     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8784 };
8785
8786 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
8787     
8788     cls: false,
8789     align: false,
8790     bgcolor: false,
8791     charoff: false,
8792     valign: false,
8793     
8794     getAutoCreate : function(){
8795         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8796         
8797         cfg = {
8798             tag: 'tr'
8799         };
8800             
8801         if(this.cls){
8802             cfg.cls = this.cls;
8803         }
8804         if(this.align){
8805             cfg.align = this.align;
8806         }
8807         if(this.bgcolor){
8808             cfg.bgcolor = this.bgcolor;
8809         }
8810         if(this.charoff){
8811             cfg.charoff = this.charoff;
8812         }
8813         if(this.valign){
8814             cfg.valign = this.valign;
8815         }
8816         
8817         return cfg;
8818     }
8819    
8820 });
8821
8822  
8823
8824  /*
8825  * - LGPL
8826  *
8827  * table body
8828  * 
8829  */
8830
8831 /**
8832  * @class Roo.bootstrap.TableBody
8833  * @extends Roo.bootstrap.Component
8834  * Bootstrap TableBody class
8835  * @cfg {String} cls element class
8836  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8837  * @cfg {String} align Aligns the content inside the element
8838  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8839  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8840  * 
8841  * @constructor
8842  * Create a new TableBody
8843  * @param {Object} config The config object
8844  */
8845
8846 Roo.bootstrap.TableBody = function(config){
8847     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8848 };
8849
8850 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
8851     
8852     cls: false,
8853     tag: false,
8854     align: false,
8855     charoff: false,
8856     valign: false,
8857     
8858     getAutoCreate : function(){
8859         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8860         
8861         cfg = {
8862             tag: 'tbody'
8863         };
8864             
8865         if (this.cls) {
8866             cfg.cls=this.cls
8867         }
8868         if(this.tag){
8869             cfg.tag = this.tag;
8870         }
8871         
8872         if(this.align){
8873             cfg.align = this.align;
8874         }
8875         if(this.charoff){
8876             cfg.charoff = this.charoff;
8877         }
8878         if(this.valign){
8879             cfg.valign = this.valign;
8880         }
8881         
8882         return cfg;
8883     }
8884     
8885     
8886 //    initEvents : function()
8887 //    {
8888 //        
8889 //        if(!this.store){
8890 //            return;
8891 //        }
8892 //        
8893 //        this.store = Roo.factory(this.store, Roo.data);
8894 //        this.store.on('load', this.onLoad, this);
8895 //        
8896 //        this.store.load();
8897 //        
8898 //    },
8899 //    
8900 //    onLoad: function () 
8901 //    {   
8902 //        this.fireEvent('load', this);
8903 //    }
8904 //    
8905 //   
8906 });
8907
8908  
8909
8910  /*
8911  * Based on:
8912  * Ext JS Library 1.1.1
8913  * Copyright(c) 2006-2007, Ext JS, LLC.
8914  *
8915  * Originally Released Under LGPL - original licence link has changed is not relivant.
8916  *
8917  * Fork - LGPL
8918  * <script type="text/javascript">
8919  */
8920
8921 // as we use this in bootstrap.
8922 Roo.namespace('Roo.form');
8923  /**
8924  * @class Roo.form.Action
8925  * Internal Class used to handle form actions
8926  * @constructor
8927  * @param {Roo.form.BasicForm} el The form element or its id
8928  * @param {Object} config Configuration options
8929  */
8930
8931  
8932  
8933 // define the action interface
8934 Roo.form.Action = function(form, options){
8935     this.form = form;
8936     this.options = options || {};
8937 };
8938 /**
8939  * Client Validation Failed
8940  * @const 
8941  */
8942 Roo.form.Action.CLIENT_INVALID = 'client';
8943 /**
8944  * Server Validation Failed
8945  * @const 
8946  */
8947 Roo.form.Action.SERVER_INVALID = 'server';
8948  /**
8949  * Connect to Server Failed
8950  * @const 
8951  */
8952 Roo.form.Action.CONNECT_FAILURE = 'connect';
8953 /**
8954  * Reading Data from Server Failed
8955  * @const 
8956  */
8957 Roo.form.Action.LOAD_FAILURE = 'load';
8958
8959 Roo.form.Action.prototype = {
8960     type : 'default',
8961     failureType : undefined,
8962     response : undefined,
8963     result : undefined,
8964
8965     // interface method
8966     run : function(options){
8967
8968     },
8969
8970     // interface method
8971     success : function(response){
8972
8973     },
8974
8975     // interface method
8976     handleResponse : function(response){
8977
8978     },
8979
8980     // default connection failure
8981     failure : function(response){
8982         
8983         this.response = response;
8984         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8985         this.form.afterAction(this, false);
8986     },
8987
8988     processResponse : function(response){
8989         this.response = response;
8990         if(!response.responseText){
8991             return true;
8992         }
8993         this.result = this.handleResponse(response);
8994         return this.result;
8995     },
8996
8997     // utility functions used internally
8998     getUrl : function(appendParams){
8999         var url = this.options.url || this.form.url || this.form.el.dom.action;
9000         if(appendParams){
9001             var p = this.getParams();
9002             if(p){
9003                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9004             }
9005         }
9006         return url;
9007     },
9008
9009     getMethod : function(){
9010         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9011     },
9012
9013     getParams : function(){
9014         var bp = this.form.baseParams;
9015         var p = this.options.params;
9016         if(p){
9017             if(typeof p == "object"){
9018                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9019             }else if(typeof p == 'string' && bp){
9020                 p += '&' + Roo.urlEncode(bp);
9021             }
9022         }else if(bp){
9023             p = Roo.urlEncode(bp);
9024         }
9025         return p;
9026     },
9027
9028     createCallback : function(){
9029         return {
9030             success: this.success,
9031             failure: this.failure,
9032             scope: this,
9033             timeout: (this.form.timeout*1000),
9034             upload: this.form.fileUpload ? this.success : undefined
9035         };
9036     }
9037 };
9038
9039 Roo.form.Action.Submit = function(form, options){
9040     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9041 };
9042
9043 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9044     type : 'submit',
9045
9046     haveProgress : false,
9047     uploadComplete : false,
9048     
9049     // uploadProgress indicator.
9050     uploadProgress : function()
9051     {
9052         if (!this.form.progressUrl) {
9053             return;
9054         }
9055         
9056         if (!this.haveProgress) {
9057             Roo.MessageBox.progress("Uploading", "Uploading");
9058         }
9059         if (this.uploadComplete) {
9060            Roo.MessageBox.hide();
9061            return;
9062         }
9063         
9064         this.haveProgress = true;
9065    
9066         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9067         
9068         var c = new Roo.data.Connection();
9069         c.request({
9070             url : this.form.progressUrl,
9071             params: {
9072                 id : uid
9073             },
9074             method: 'GET',
9075             success : function(req){
9076                //console.log(data);
9077                 var rdata = false;
9078                 var edata;
9079                 try  {
9080                    rdata = Roo.decode(req.responseText)
9081                 } catch (e) {
9082                     Roo.log("Invalid data from server..");
9083                     Roo.log(edata);
9084                     return;
9085                 }
9086                 if (!rdata || !rdata.success) {
9087                     Roo.log(rdata);
9088                     Roo.MessageBox.alert(Roo.encode(rdata));
9089                     return;
9090                 }
9091                 var data = rdata.data;
9092                 
9093                 if (this.uploadComplete) {
9094                    Roo.MessageBox.hide();
9095                    return;
9096                 }
9097                    
9098                 if (data){
9099                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9100                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9101                     );
9102                 }
9103                 this.uploadProgress.defer(2000,this);
9104             },
9105        
9106             failure: function(data) {
9107                 Roo.log('progress url failed ');
9108                 Roo.log(data);
9109             },
9110             scope : this
9111         });
9112            
9113     },
9114     
9115     
9116     run : function()
9117     {
9118         // run get Values on the form, so it syncs any secondary forms.
9119         this.form.getValues();
9120         
9121         var o = this.options;
9122         var method = this.getMethod();
9123         var isPost = method == 'POST';
9124         if(o.clientValidation === false || this.form.isValid()){
9125             
9126             if (this.form.progressUrl) {
9127                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9128                     (new Date() * 1) + '' + Math.random());
9129                     
9130             } 
9131             
9132             
9133             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9134                 form:this.form.el.dom,
9135                 url:this.getUrl(!isPost),
9136                 method: method,
9137                 params:isPost ? this.getParams() : null,
9138                 isUpload: this.form.fileUpload,
9139                 formData : this.form.formData
9140             }));
9141             
9142             this.uploadProgress();
9143
9144         }else if (o.clientValidation !== false){ // client validation failed
9145             this.failureType = Roo.form.Action.CLIENT_INVALID;
9146             this.form.afterAction(this, false);
9147         }
9148     },
9149
9150     success : function(response)
9151     {
9152         this.uploadComplete= true;
9153         if (this.haveProgress) {
9154             Roo.MessageBox.hide();
9155         }
9156         
9157         
9158         var result = this.processResponse(response);
9159         if(result === true || result.success){
9160             this.form.afterAction(this, true);
9161             return;
9162         }
9163         if(result.errors){
9164             this.form.markInvalid(result.errors);
9165             this.failureType = Roo.form.Action.SERVER_INVALID;
9166         }
9167         this.form.afterAction(this, false);
9168     },
9169     failure : function(response)
9170     {
9171         this.uploadComplete= true;
9172         if (this.haveProgress) {
9173             Roo.MessageBox.hide();
9174         }
9175         
9176         this.response = response;
9177         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9178         this.form.afterAction(this, false);
9179     },
9180     
9181     handleResponse : function(response){
9182         if(this.form.errorReader){
9183             var rs = this.form.errorReader.read(response);
9184             var errors = [];
9185             if(rs.records){
9186                 for(var i = 0, len = rs.records.length; i < len; i++) {
9187                     var r = rs.records[i];
9188                     errors[i] = r.data;
9189                 }
9190             }
9191             if(errors.length < 1){
9192                 errors = null;
9193             }
9194             return {
9195                 success : rs.success,
9196                 errors : errors
9197             };
9198         }
9199         var ret = false;
9200         try {
9201             ret = Roo.decode(response.responseText);
9202         } catch (e) {
9203             ret = {
9204                 success: false,
9205                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9206                 errors : []
9207             };
9208         }
9209         return ret;
9210         
9211     }
9212 });
9213
9214
9215 Roo.form.Action.Load = function(form, options){
9216     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9217     this.reader = this.form.reader;
9218 };
9219
9220 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9221     type : 'load',
9222
9223     run : function(){
9224         
9225         Roo.Ajax.request(Roo.apply(
9226                 this.createCallback(), {
9227                     method:this.getMethod(),
9228                     url:this.getUrl(false),
9229                     params:this.getParams()
9230         }));
9231     },
9232
9233     success : function(response){
9234         
9235         var result = this.processResponse(response);
9236         if(result === true || !result.success || !result.data){
9237             this.failureType = Roo.form.Action.LOAD_FAILURE;
9238             this.form.afterAction(this, false);
9239             return;
9240         }
9241         this.form.clearInvalid();
9242         this.form.setValues(result.data);
9243         this.form.afterAction(this, true);
9244     },
9245
9246     handleResponse : function(response){
9247         if(this.form.reader){
9248             var rs = this.form.reader.read(response);
9249             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9250             return {
9251                 success : rs.success,
9252                 data : data
9253             };
9254         }
9255         return Roo.decode(response.responseText);
9256     }
9257 });
9258
9259 Roo.form.Action.ACTION_TYPES = {
9260     'load' : Roo.form.Action.Load,
9261     'submit' : Roo.form.Action.Submit
9262 };/*
9263  * - LGPL
9264  *
9265  * form
9266  *
9267  */
9268
9269 /**
9270  * @class Roo.bootstrap.Form
9271  * @extends Roo.bootstrap.Component
9272  * Bootstrap Form class
9273  * @cfg {String} method  GET | POST (default POST)
9274  * @cfg {String} labelAlign top | left (default top)
9275  * @cfg {String} align left  | right - for navbars
9276  * @cfg {Boolean} loadMask load mask when submit (default true)
9277
9278  *
9279  * @constructor
9280  * Create a new Form
9281  * @param {Object} config The config object
9282  */
9283
9284
9285 Roo.bootstrap.Form = function(config){
9286     
9287     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9288     
9289     Roo.bootstrap.Form.popover.apply();
9290     
9291     this.addEvents({
9292         /**
9293          * @event clientvalidation
9294          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9295          * @param {Form} this
9296          * @param {Boolean} valid true if the form has passed client-side validation
9297          */
9298         clientvalidation: true,
9299         /**
9300          * @event beforeaction
9301          * Fires before any action is performed. Return false to cancel the action.
9302          * @param {Form} this
9303          * @param {Action} action The action to be performed
9304          */
9305         beforeaction: true,
9306         /**
9307          * @event actionfailed
9308          * Fires when an action fails.
9309          * @param {Form} this
9310          * @param {Action} action The action that failed
9311          */
9312         actionfailed : true,
9313         /**
9314          * @event actioncomplete
9315          * Fires when an action is completed.
9316          * @param {Form} this
9317          * @param {Action} action The action that completed
9318          */
9319         actioncomplete : true
9320     });
9321 };
9322
9323 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9324
9325      /**
9326      * @cfg {String} method
9327      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9328      */
9329     method : 'POST',
9330     /**
9331      * @cfg {String} url
9332      * The URL to use for form actions if one isn't supplied in the action options.
9333      */
9334     /**
9335      * @cfg {Boolean} fileUpload
9336      * Set to true if this form is a file upload.
9337      */
9338
9339     /**
9340      * @cfg {Object} baseParams
9341      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9342      */
9343
9344     /**
9345      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9346      */
9347     timeout: 30,
9348     /**
9349      * @cfg {Sting} align (left|right) for navbar forms
9350      */
9351     align : 'left',
9352
9353     // private
9354     activeAction : null,
9355
9356     /**
9357      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9358      * element by passing it or its id or mask the form itself by passing in true.
9359      * @type Mixed
9360      */
9361     waitMsgTarget : false,
9362
9363     loadMask : true,
9364     
9365     /**
9366      * @cfg {Boolean} errorMask (true|false) default false
9367      */
9368     errorMask : false,
9369     
9370     /**
9371      * @cfg {Number} maskOffset Default 100
9372      */
9373     maskOffset : 100,
9374     
9375     /**
9376      * @cfg {Boolean} maskBody
9377      */
9378     maskBody : false,
9379
9380     getAutoCreate : function(){
9381
9382         var cfg = {
9383             tag: 'form',
9384             method : this.method || 'POST',
9385             id : this.id || Roo.id(),
9386             cls : ''
9387         };
9388         if (this.parent().xtype.match(/^Nav/)) {
9389             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9390
9391         }
9392
9393         if (this.labelAlign == 'left' ) {
9394             cfg.cls += ' form-horizontal';
9395         }
9396
9397
9398         return cfg;
9399     },
9400     initEvents : function()
9401     {
9402         this.el.on('submit', this.onSubmit, this);
9403         // this was added as random key presses on the form where triggering form submit.
9404         this.el.on('keypress', function(e) {
9405             if (e.getCharCode() != 13) {
9406                 return true;
9407             }
9408             // we might need to allow it for textareas.. and some other items.
9409             // check e.getTarget().
9410
9411             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9412                 return true;
9413             }
9414
9415             Roo.log("keypress blocked");
9416
9417             e.preventDefault();
9418             return false;
9419         });
9420         
9421     },
9422     // private
9423     onSubmit : function(e){
9424         e.stopEvent();
9425     },
9426
9427      /**
9428      * Returns true if client-side validation on the form is successful.
9429      * @return Boolean
9430      */
9431     isValid : function(){
9432         var items = this.getItems();
9433         var valid = true;
9434         var target = false;
9435         
9436         items.each(function(f){
9437             
9438             if(f.validate()){
9439                 return;
9440             }
9441             
9442             Roo.log('invalid field: ' + f.name);
9443             
9444             valid = false;
9445
9446             if(!target && f.el.isVisible(true)){
9447                 target = f;
9448             }
9449            
9450         });
9451         
9452         if(this.errorMask && !valid){
9453             Roo.bootstrap.Form.popover.mask(this, target);
9454         }
9455         
9456         return valid;
9457     },
9458     
9459     /**
9460      * Returns true if any fields in this form have changed since their original load.
9461      * @return Boolean
9462      */
9463     isDirty : function(){
9464         var dirty = false;
9465         var items = this.getItems();
9466         items.each(function(f){
9467            if(f.isDirty()){
9468                dirty = true;
9469                return false;
9470            }
9471            return true;
9472         });
9473         return dirty;
9474     },
9475      /**
9476      * Performs a predefined action (submit or load) or custom actions you define on this form.
9477      * @param {String} actionName The name of the action type
9478      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9479      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9480      * accept other config options):
9481      * <pre>
9482 Property          Type             Description
9483 ----------------  ---------------  ----------------------------------------------------------------------------------
9484 url               String           The url for the action (defaults to the form's url)
9485 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9486 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9487 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9488                                    validate the form on the client (defaults to false)
9489      * </pre>
9490      * @return {BasicForm} this
9491      */
9492     doAction : function(action, options){
9493         if(typeof action == 'string'){
9494             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9495         }
9496         if(this.fireEvent('beforeaction', this, action) !== false){
9497             this.beforeAction(action);
9498             action.run.defer(100, action);
9499         }
9500         return this;
9501     },
9502
9503     // private
9504     beforeAction : function(action){
9505         var o = action.options;
9506         
9507         if(this.loadMask){
9508             
9509             if(this.maskBody){
9510                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9511             } else {
9512                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9513             }
9514         }
9515         // not really supported yet.. ??
9516
9517         //if(this.waitMsgTarget === true){
9518         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9519         //}else if(this.waitMsgTarget){
9520         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9521         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9522         //}else {
9523         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9524        // }
9525
9526     },
9527
9528     // private
9529     afterAction : function(action, success){
9530         this.activeAction = null;
9531         var o = action.options;
9532
9533         if(this.loadMask){
9534             
9535             if(this.maskBody){
9536                 Roo.get(document.body).unmask();
9537             } else {
9538                 this.el.unmask();
9539             }
9540         }
9541         
9542         //if(this.waitMsgTarget === true){
9543 //            this.el.unmask();
9544         //}else if(this.waitMsgTarget){
9545         //    this.waitMsgTarget.unmask();
9546         //}else{
9547         //    Roo.MessageBox.updateProgress(1);
9548         //    Roo.MessageBox.hide();
9549        // }
9550         //
9551         if(success){
9552             if(o.reset){
9553                 this.reset();
9554             }
9555             Roo.callback(o.success, o.scope, [this, action]);
9556             this.fireEvent('actioncomplete', this, action);
9557
9558         }else{
9559
9560             // failure condition..
9561             // we have a scenario where updates need confirming.
9562             // eg. if a locking scenario exists..
9563             // we look for { errors : { needs_confirm : true }} in the response.
9564             if (
9565                 (typeof(action.result) != 'undefined')  &&
9566                 (typeof(action.result.errors) != 'undefined')  &&
9567                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9568            ){
9569                 var _t = this;
9570                 Roo.log("not supported yet");
9571                  /*
9572
9573                 Roo.MessageBox.confirm(
9574                     "Change requires confirmation",
9575                     action.result.errorMsg,
9576                     function(r) {
9577                         if (r != 'yes') {
9578                             return;
9579                         }
9580                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9581                     }
9582
9583                 );
9584                 */
9585
9586
9587                 return;
9588             }
9589
9590             Roo.callback(o.failure, o.scope, [this, action]);
9591             // show an error message if no failed handler is set..
9592             if (!this.hasListener('actionfailed')) {
9593                 Roo.log("need to add dialog support");
9594                 /*
9595                 Roo.MessageBox.alert("Error",
9596                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9597                         action.result.errorMsg :
9598                         "Saving Failed, please check your entries or try again"
9599                 );
9600                 */
9601             }
9602
9603             this.fireEvent('actionfailed', this, action);
9604         }
9605
9606     },
9607     /**
9608      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9609      * @param {String} id The value to search for
9610      * @return Field
9611      */
9612     findField : function(id){
9613         var items = this.getItems();
9614         var field = items.get(id);
9615         if(!field){
9616              items.each(function(f){
9617                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9618                     field = f;
9619                     return false;
9620                 }
9621                 return true;
9622             });
9623         }
9624         return field || null;
9625     },
9626      /**
9627      * Mark fields in this form invalid in bulk.
9628      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9629      * @return {BasicForm} this
9630      */
9631     markInvalid : function(errors){
9632         if(errors instanceof Array){
9633             for(var i = 0, len = errors.length; i < len; i++){
9634                 var fieldError = errors[i];
9635                 var f = this.findField(fieldError.id);
9636                 if(f){
9637                     f.markInvalid(fieldError.msg);
9638                 }
9639             }
9640         }else{
9641             var field, id;
9642             for(id in errors){
9643                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9644                     field.markInvalid(errors[id]);
9645                 }
9646             }
9647         }
9648         //Roo.each(this.childForms || [], function (f) {
9649         //    f.markInvalid(errors);
9650         //});
9651
9652         return this;
9653     },
9654
9655     /**
9656      * Set values for fields in this form in bulk.
9657      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9658      * @return {BasicForm} this
9659      */
9660     setValues : function(values){
9661         if(values instanceof Array){ // array of objects
9662             for(var i = 0, len = values.length; i < len; i++){
9663                 var v = values[i];
9664                 var f = this.findField(v.id);
9665                 if(f){
9666                     f.setValue(v.value);
9667                     if(this.trackResetOnLoad){
9668                         f.originalValue = f.getValue();
9669                     }
9670                 }
9671             }
9672         }else{ // object hash
9673             var field, id;
9674             for(id in values){
9675                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9676
9677                     if (field.setFromData &&
9678                         field.valueField &&
9679                         field.displayField &&
9680                         // combos' with local stores can
9681                         // be queried via setValue()
9682                         // to set their value..
9683                         (field.store && !field.store.isLocal)
9684                         ) {
9685                         // it's a combo
9686                         var sd = { };
9687                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9688                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9689                         field.setFromData(sd);
9690
9691                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9692                         
9693                         field.setFromData(values);
9694                         
9695                     } else {
9696                         field.setValue(values[id]);
9697                     }
9698
9699
9700                     if(this.trackResetOnLoad){
9701                         field.originalValue = field.getValue();
9702                     }
9703                 }
9704             }
9705         }
9706
9707         //Roo.each(this.childForms || [], function (f) {
9708         //    f.setValues(values);
9709         //});
9710
9711         return this;
9712     },
9713
9714     /**
9715      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9716      * they are returned as an array.
9717      * @param {Boolean} asString
9718      * @return {Object}
9719      */
9720     getValues : function(asString){
9721         //if (this.childForms) {
9722             // copy values from the child forms
9723         //    Roo.each(this.childForms, function (f) {
9724         //        this.setValues(f.getValues());
9725         //    }, this);
9726         //}
9727
9728
9729
9730         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9731         if(asString === true){
9732             return fs;
9733         }
9734         return Roo.urlDecode(fs);
9735     },
9736
9737     /**
9738      * Returns the fields in this form as an object with key/value pairs.
9739      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9740      * @return {Object}
9741      */
9742     getFieldValues : function(with_hidden)
9743     {
9744         var items = this.getItems();
9745         var ret = {};
9746         items.each(function(f){
9747             
9748             if (!f.getName()) {
9749                 return;
9750             }
9751             
9752             var v = f.getValue();
9753             
9754             if (f.inputType =='radio') {
9755                 if (typeof(ret[f.getName()]) == 'undefined') {
9756                     ret[f.getName()] = ''; // empty..
9757                 }
9758
9759                 if (!f.el.dom.checked) {
9760                     return;
9761
9762                 }
9763                 v = f.el.dom.value;
9764
9765             }
9766             
9767             if(f.xtype == 'MoneyField'){
9768                 ret[f.currencyName] = f.getCurrency();
9769             }
9770
9771             // not sure if this supported any more..
9772             if ((typeof(v) == 'object') && f.getRawValue) {
9773                 v = f.getRawValue() ; // dates..
9774             }
9775             // combo boxes where name != hiddenName...
9776             if (f.name !== false && f.name != '' && f.name != f.getName()) {
9777                 ret[f.name] = f.getRawValue();
9778             }
9779             ret[f.getName()] = v;
9780         });
9781
9782         return ret;
9783     },
9784
9785     /**
9786      * Clears all invalid messages in this form.
9787      * @return {BasicForm} this
9788      */
9789     clearInvalid : function(){
9790         var items = this.getItems();
9791
9792         items.each(function(f){
9793            f.clearInvalid();
9794         });
9795
9796         return this;
9797     },
9798
9799     /**
9800      * Resets this form.
9801      * @return {BasicForm} this
9802      */
9803     reset : function(){
9804         var items = this.getItems();
9805         items.each(function(f){
9806             f.reset();
9807         });
9808
9809         Roo.each(this.childForms || [], function (f) {
9810             f.reset();
9811         });
9812
9813
9814         return this;
9815     },
9816     
9817     getItems : function()
9818     {
9819         var r=new Roo.util.MixedCollection(false, function(o){
9820             return o.id || (o.id = Roo.id());
9821         });
9822         var iter = function(el) {
9823             if (el.inputEl) {
9824                 r.add(el);
9825             }
9826             if (!el.items) {
9827                 return;
9828             }
9829             Roo.each(el.items,function(e) {
9830                 iter(e);
9831             });
9832         };
9833
9834         iter(this);
9835         return r;
9836     },
9837     
9838     hideFields : function(items)
9839     {
9840         Roo.each(items, function(i){
9841             
9842             var f = this.findField(i);
9843             
9844             if(!f){
9845                 return;
9846             }
9847             
9848             f.hide();
9849             
9850         }, this);
9851     },
9852     
9853     showFields : function(items)
9854     {
9855         Roo.each(items, function(i){
9856             
9857             var f = this.findField(i);
9858             
9859             if(!f){
9860                 return;
9861             }
9862             
9863             f.show();
9864             
9865         }, this);
9866     }
9867
9868 });
9869
9870 Roo.apply(Roo.bootstrap.Form, {
9871     
9872     popover : {
9873         
9874         padding : 5,
9875         
9876         isApplied : false,
9877         
9878         isMasked : false,
9879         
9880         form : false,
9881         
9882         target : false,
9883         
9884         toolTip : false,
9885         
9886         intervalID : false,
9887         
9888         maskEl : false,
9889         
9890         apply : function()
9891         {
9892             if(this.isApplied){
9893                 return;
9894             }
9895             
9896             this.maskEl = {
9897                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9898                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
9899                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
9900                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
9901             };
9902             
9903             this.maskEl.top.enableDisplayMode("block");
9904             this.maskEl.left.enableDisplayMode("block");
9905             this.maskEl.bottom.enableDisplayMode("block");
9906             this.maskEl.right.enableDisplayMode("block");
9907             
9908             this.toolTip = new Roo.bootstrap.Tooltip({
9909                 cls : 'roo-form-error-popover',
9910                 alignment : {
9911                     'left' : ['r-l', [-2,0], 'right'],
9912                     'right' : ['l-r', [2,0], 'left'],
9913                     'bottom' : ['tl-bl', [0,2], 'top'],
9914                     'top' : [ 'bl-tl', [0,-2], 'bottom']
9915                 }
9916             });
9917             
9918             this.toolTip.render(Roo.get(document.body));
9919
9920             this.toolTip.el.enableDisplayMode("block");
9921             
9922             Roo.get(document.body).on('click', function(){
9923                 this.unmask();
9924             }, this);
9925             
9926             Roo.get(document.body).on('touchstart', function(){
9927                 this.unmask();
9928             }, this);
9929             
9930             this.isApplied = true
9931         },
9932         
9933         mask : function(form, target)
9934         {
9935             this.form = form;
9936             
9937             this.target = target;
9938             
9939             if(!this.form.errorMask || !target.el){
9940                 return;
9941             }
9942             
9943             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9944             
9945             Roo.log(scrollable);
9946             
9947             var ot = this.target.el.calcOffsetsTo(scrollable);
9948             
9949             var scrollTo = ot[1] - this.form.maskOffset;
9950             
9951             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9952             
9953             scrollable.scrollTo('top', scrollTo);
9954             
9955             var box = this.target.el.getBox();
9956             Roo.log(box);
9957             var zIndex = Roo.bootstrap.Modal.zIndex++;
9958
9959             
9960             this.maskEl.top.setStyle('position', 'absolute');
9961             this.maskEl.top.setStyle('z-index', zIndex);
9962             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
9963             this.maskEl.top.setLeft(0);
9964             this.maskEl.top.setTop(0);
9965             this.maskEl.top.show();
9966             
9967             this.maskEl.left.setStyle('position', 'absolute');
9968             this.maskEl.left.setStyle('z-index', zIndex);
9969             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
9970             this.maskEl.left.setLeft(0);
9971             this.maskEl.left.setTop(box.y - this.padding);
9972             this.maskEl.left.show();
9973
9974             this.maskEl.bottom.setStyle('position', 'absolute');
9975             this.maskEl.bottom.setStyle('z-index', zIndex);
9976             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
9977             this.maskEl.bottom.setLeft(0);
9978             this.maskEl.bottom.setTop(box.bottom + this.padding);
9979             this.maskEl.bottom.show();
9980
9981             this.maskEl.right.setStyle('position', 'absolute');
9982             this.maskEl.right.setStyle('z-index', zIndex);
9983             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
9984             this.maskEl.right.setLeft(box.right + this.padding);
9985             this.maskEl.right.setTop(box.y - this.padding);
9986             this.maskEl.right.show();
9987
9988             this.toolTip.bindEl = this.target.el;
9989
9990             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
9991
9992             var tip = this.target.blankText;
9993
9994             if(this.target.getValue() !== '' ) {
9995                 
9996                 if (this.target.invalidText.length) {
9997                     tip = this.target.invalidText;
9998                 } else if (this.target.regexText.length){
9999                     tip = this.target.regexText;
10000                 }
10001             }
10002
10003             this.toolTip.show(tip);
10004
10005             this.intervalID = window.setInterval(function() {
10006                 Roo.bootstrap.Form.popover.unmask();
10007             }, 10000);
10008
10009             window.onwheel = function(){ return false;};
10010             
10011             (function(){ this.isMasked = true; }).defer(500, this);
10012             
10013         },
10014         
10015         unmask : function()
10016         {
10017             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10018                 return;
10019             }
10020             
10021             this.maskEl.top.setStyle('position', 'absolute');
10022             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10023             this.maskEl.top.hide();
10024
10025             this.maskEl.left.setStyle('position', 'absolute');
10026             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10027             this.maskEl.left.hide();
10028
10029             this.maskEl.bottom.setStyle('position', 'absolute');
10030             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10031             this.maskEl.bottom.hide();
10032
10033             this.maskEl.right.setStyle('position', 'absolute');
10034             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10035             this.maskEl.right.hide();
10036             
10037             this.toolTip.hide();
10038             
10039             this.toolTip.el.hide();
10040             
10041             window.onwheel = function(){ return true;};
10042             
10043             if(this.intervalID){
10044                 window.clearInterval(this.intervalID);
10045                 this.intervalID = false;
10046             }
10047             
10048             this.isMasked = false;
10049             
10050         }
10051         
10052     }
10053     
10054 });
10055
10056 /*
10057  * Based on:
10058  * Ext JS Library 1.1.1
10059  * Copyright(c) 2006-2007, Ext JS, LLC.
10060  *
10061  * Originally Released Under LGPL - original licence link has changed is not relivant.
10062  *
10063  * Fork - LGPL
10064  * <script type="text/javascript">
10065  */
10066 /**
10067  * @class Roo.form.VTypes
10068  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10069  * @singleton
10070  */
10071 Roo.form.VTypes = function(){
10072     // closure these in so they are only created once.
10073     var alpha = /^[a-zA-Z_]+$/;
10074     var alphanum = /^[a-zA-Z0-9_]+$/;
10075     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10076     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10077
10078     // All these messages and functions are configurable
10079     return {
10080         /**
10081          * The function used to validate email addresses
10082          * @param {String} value The email address
10083          */
10084         'email' : function(v){
10085             return email.test(v);
10086         },
10087         /**
10088          * The error text to display when the email validation function returns false
10089          * @type String
10090          */
10091         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10092         /**
10093          * The keystroke filter mask to be applied on email input
10094          * @type RegExp
10095          */
10096         'emailMask' : /[a-z0-9_\.\-@]/i,
10097
10098         /**
10099          * The function used to validate URLs
10100          * @param {String} value The URL
10101          */
10102         'url' : function(v){
10103             return url.test(v);
10104         },
10105         /**
10106          * The error text to display when the url validation function returns false
10107          * @type String
10108          */
10109         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10110         
10111         /**
10112          * The function used to validate alpha values
10113          * @param {String} value The value
10114          */
10115         'alpha' : function(v){
10116             return alpha.test(v);
10117         },
10118         /**
10119          * The error text to display when the alpha validation function returns false
10120          * @type String
10121          */
10122         'alphaText' : 'This field should only contain letters and _',
10123         /**
10124          * The keystroke filter mask to be applied on alpha input
10125          * @type RegExp
10126          */
10127         'alphaMask' : /[a-z_]/i,
10128
10129         /**
10130          * The function used to validate alphanumeric values
10131          * @param {String} value The value
10132          */
10133         'alphanum' : function(v){
10134             return alphanum.test(v);
10135         },
10136         /**
10137          * The error text to display when the alphanumeric validation function returns false
10138          * @type String
10139          */
10140         'alphanumText' : 'This field should only contain letters, numbers and _',
10141         /**
10142          * The keystroke filter mask to be applied on alphanumeric input
10143          * @type RegExp
10144          */
10145         'alphanumMask' : /[a-z0-9_]/i
10146     };
10147 }();/*
10148  * - LGPL
10149  *
10150  * Input
10151  * 
10152  */
10153
10154 /**
10155  * @class Roo.bootstrap.Input
10156  * @extends Roo.bootstrap.Component
10157  * Bootstrap Input class
10158  * @cfg {Boolean} disabled is it disabled
10159  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
10160  * @cfg {String} name name of the input
10161  * @cfg {string} fieldLabel - the label associated
10162  * @cfg {string} placeholder - placeholder to put in text.
10163  * @cfg {string}  before - input group add on before
10164  * @cfg {string} after - input group add on after
10165  * @cfg {string} size - (lg|sm) or leave empty..
10166  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10167  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10168  * @cfg {Number} md colspan out of 12 for computer-sized screens
10169  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10170  * @cfg {string} value default value of the input
10171  * @cfg {Number} labelWidth set the width of label 
10172  * @cfg {Number} labellg set the width of label (1-12)
10173  * @cfg {Number} labelmd set the width of label (1-12)
10174  * @cfg {Number} labelsm set the width of label (1-12)
10175  * @cfg {Number} labelxs set the width of label (1-12)
10176  * @cfg {String} labelAlign (top|left)
10177  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10178  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10179  * @cfg {String} indicatorpos (left|right) default left
10180  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10181  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10182  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10183
10184  * @cfg {String} align (left|center|right) Default left
10185  * @cfg {Boolean} forceFeedback (true|false) Default false
10186  * 
10187  * @constructor
10188  * Create a new Input
10189  * @param {Object} config The config object
10190  */
10191
10192 Roo.bootstrap.Input = function(config){
10193     
10194     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10195     
10196     this.addEvents({
10197         /**
10198          * @event focus
10199          * Fires when this field receives input focus.
10200          * @param {Roo.form.Field} this
10201          */
10202         focus : true,
10203         /**
10204          * @event blur
10205          * Fires when this field loses input focus.
10206          * @param {Roo.form.Field} this
10207          */
10208         blur : true,
10209         /**
10210          * @event specialkey
10211          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10212          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10213          * @param {Roo.form.Field} this
10214          * @param {Roo.EventObject} e The event object
10215          */
10216         specialkey : true,
10217         /**
10218          * @event change
10219          * Fires just before the field blurs if the field value has changed.
10220          * @param {Roo.form.Field} this
10221          * @param {Mixed} newValue The new value
10222          * @param {Mixed} oldValue The original value
10223          */
10224         change : true,
10225         /**
10226          * @event invalid
10227          * Fires after the field has been marked as invalid.
10228          * @param {Roo.form.Field} this
10229          * @param {String} msg The validation message
10230          */
10231         invalid : true,
10232         /**
10233          * @event valid
10234          * Fires after the field has been validated with no errors.
10235          * @param {Roo.form.Field} this
10236          */
10237         valid : true,
10238          /**
10239          * @event keyup
10240          * Fires after the key up
10241          * @param {Roo.form.Field} this
10242          * @param {Roo.EventObject}  e The event Object
10243          */
10244         keyup : true
10245     });
10246 };
10247
10248 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10249      /**
10250      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10251       automatic validation (defaults to "keyup").
10252      */
10253     validationEvent : "keyup",
10254      /**
10255      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10256      */
10257     validateOnBlur : true,
10258     /**
10259      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10260      */
10261     validationDelay : 250,
10262      /**
10263      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10264      */
10265     focusClass : "x-form-focus",  // not needed???
10266     
10267        
10268     /**
10269      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10270      */
10271     invalidClass : "has-warning",
10272     
10273     /**
10274      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10275      */
10276     validClass : "has-success",
10277     
10278     /**
10279      * @cfg {Boolean} hasFeedback (true|false) default true
10280      */
10281     hasFeedback : true,
10282     
10283     /**
10284      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10285      */
10286     invalidFeedbackClass : "glyphicon-warning-sign",
10287     
10288     /**
10289      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10290      */
10291     validFeedbackClass : "glyphicon-ok",
10292     
10293     /**
10294      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10295      */
10296     selectOnFocus : false,
10297     
10298      /**
10299      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10300      */
10301     maskRe : null,
10302        /**
10303      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10304      */
10305     vtype : null,
10306     
10307       /**
10308      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10309      */
10310     disableKeyFilter : false,
10311     
10312        /**
10313      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10314      */
10315     disabled : false,
10316      /**
10317      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10318      */
10319     allowBlank : true,
10320     /**
10321      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10322      */
10323     blankText : "Please complete this mandatory field",
10324     
10325      /**
10326      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10327      */
10328     minLength : 0,
10329     /**
10330      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10331      */
10332     maxLength : Number.MAX_VALUE,
10333     /**
10334      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10335      */
10336     minLengthText : "The minimum length for this field is {0}",
10337     /**
10338      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10339      */
10340     maxLengthText : "The maximum length for this field is {0}",
10341   
10342     
10343     /**
10344      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10345      * If available, this function will be called only after the basic validators all return true, and will be passed the
10346      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10347      */
10348     validator : null,
10349     /**
10350      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10351      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10352      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10353      */
10354     regex : null,
10355     /**
10356      * @cfg {String} regexText -- Depricated - use Invalid Text
10357      */
10358     regexText : "",
10359     
10360     /**
10361      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10362      */
10363     invalidText : "",
10364     
10365     
10366     
10367     autocomplete: false,
10368     
10369     
10370     fieldLabel : '',
10371     inputType : 'text',
10372     
10373     name : false,
10374     placeholder: false,
10375     before : false,
10376     after : false,
10377     size : false,
10378     hasFocus : false,
10379     preventMark: false,
10380     isFormField : true,
10381     value : '',
10382     labelWidth : 2,
10383     labelAlign : false,
10384     readOnly : false,
10385     align : false,
10386     formatedValue : false,
10387     forceFeedback : false,
10388     
10389     indicatorpos : 'left',
10390     
10391     labellg : 0,
10392     labelmd : 0,
10393     labelsm : 0,
10394     labelxs : 0,
10395     
10396     capture : '',
10397     accept : '',
10398     
10399     parentLabelAlign : function()
10400     {
10401         var parent = this;
10402         while (parent.parent()) {
10403             parent = parent.parent();
10404             if (typeof(parent.labelAlign) !='undefined') {
10405                 return parent.labelAlign;
10406             }
10407         }
10408         return 'left';
10409         
10410     },
10411     
10412     getAutoCreate : function()
10413     {
10414         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10415         
10416         var id = Roo.id();
10417         
10418         var cfg = {};
10419         
10420         if(this.inputType != 'hidden'){
10421             cfg.cls = 'form-group' //input-group
10422         }
10423         
10424         var input =  {
10425             tag: 'input',
10426             id : id,
10427             type : this.inputType,
10428             value : this.value,
10429             cls : 'form-control',
10430             placeholder : this.placeholder || '',
10431             autocomplete : this.autocomplete || 'new-password'
10432         };
10433         
10434         if(this.capture.length){
10435             input.capture = this.capture;
10436         }
10437         
10438         if(this.accept.length){
10439             input.accept = this.accept + "/*";
10440         }
10441         
10442         if(this.align){
10443             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10444         }
10445         
10446         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10447             input.maxLength = this.maxLength;
10448         }
10449         
10450         if (this.disabled) {
10451             input.disabled=true;
10452         }
10453         
10454         if (this.readOnly) {
10455             input.readonly=true;
10456         }
10457         
10458         if (this.name) {
10459             input.name = this.name;
10460         }
10461         
10462         if (this.size) {
10463             input.cls += ' input-' + this.size;
10464         }
10465         
10466         var settings=this;
10467         ['xs','sm','md','lg'].map(function(size){
10468             if (settings[size]) {
10469                 cfg.cls += ' col-' + size + '-' + settings[size];
10470             }
10471         });
10472         
10473         var inputblock = input;
10474         
10475         var feedback = {
10476             tag: 'span',
10477             cls: 'glyphicon form-control-feedback'
10478         };
10479             
10480         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10481             
10482             inputblock = {
10483                 cls : 'has-feedback',
10484                 cn :  [
10485                     input,
10486                     feedback
10487                 ] 
10488             };  
10489         }
10490         
10491         if (this.before || this.after) {
10492             
10493             inputblock = {
10494                 cls : 'input-group',
10495                 cn :  [] 
10496             };
10497             
10498             if (this.before && typeof(this.before) == 'string') {
10499                 
10500                 inputblock.cn.push({
10501                     tag :'span',
10502                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10503                     html : this.before
10504                 });
10505             }
10506             if (this.before && typeof(this.before) == 'object') {
10507                 this.before = Roo.factory(this.before);
10508                 
10509                 inputblock.cn.push({
10510                     tag :'span',
10511                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
10512                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10513                 });
10514             }
10515             
10516             inputblock.cn.push(input);
10517             
10518             if (this.after && typeof(this.after) == 'string') {
10519                 inputblock.cn.push({
10520                     tag :'span',
10521                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10522                     html : this.after
10523                 });
10524             }
10525             if (this.after && typeof(this.after) == 'object') {
10526                 this.after = Roo.factory(this.after);
10527                 
10528                 inputblock.cn.push({
10529                     tag :'span',
10530                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
10531                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10532                 });
10533             }
10534             
10535             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10536                 inputblock.cls += ' has-feedback';
10537                 inputblock.cn.push(feedback);
10538             }
10539         };
10540         var indicator = {
10541             tag : 'i',
10542             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10543             tooltip : 'This field is required'
10544         };
10545         if (Roo.bootstrap.version == 4) {
10546             indicator = {
10547                 tag : 'i',
10548                 style : 'display-none'
10549             };
10550         }
10551         if (align ==='left' && this.fieldLabel.length) {
10552             
10553             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10554             
10555             cfg.cn = [
10556                 indicator,
10557                 {
10558                     tag: 'label',
10559                     'for' :  id,
10560                     cls : 'control-label col-form-label',
10561                     html : this.fieldLabel
10562
10563                 },
10564                 {
10565                     cls : "", 
10566                     cn: [
10567                         inputblock
10568                     ]
10569                 }
10570             ];
10571             
10572             var labelCfg = cfg.cn[1];
10573             var contentCfg = cfg.cn[2];
10574             
10575             if(this.indicatorpos == 'right'){
10576                 cfg.cn = [
10577                     {
10578                         tag: 'label',
10579                         'for' :  id,
10580                         cls : 'control-label col-form-label',
10581                         cn : [
10582                             {
10583                                 tag : 'span',
10584                                 html : this.fieldLabel
10585                             },
10586                             indicator
10587                         ]
10588                     },
10589                     {
10590                         cls : "",
10591                         cn: [
10592                             inputblock
10593                         ]
10594                     }
10595
10596                 ];
10597                 
10598                 labelCfg = cfg.cn[0];
10599                 contentCfg = cfg.cn[1];
10600             
10601             }
10602             
10603             if(this.labelWidth > 12){
10604                 labelCfg.style = "width: " + this.labelWidth + 'px';
10605             }
10606             
10607             if(this.labelWidth < 13 && this.labelmd == 0){
10608                 this.labelmd = this.labelWidth;
10609             }
10610             
10611             if(this.labellg > 0){
10612                 labelCfg.cls += ' col-lg-' + this.labellg;
10613                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10614             }
10615             
10616             if(this.labelmd > 0){
10617                 labelCfg.cls += ' col-md-' + this.labelmd;
10618                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10619             }
10620             
10621             if(this.labelsm > 0){
10622                 labelCfg.cls += ' col-sm-' + this.labelsm;
10623                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10624             }
10625             
10626             if(this.labelxs > 0){
10627                 labelCfg.cls += ' col-xs-' + this.labelxs;
10628                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10629             }
10630             
10631             
10632         } else if ( this.fieldLabel.length) {
10633                 
10634             cfg.cn = [
10635                 {
10636                     tag : 'i',
10637                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10638                     tooltip : 'This field is required'
10639                 },
10640                 {
10641                     tag: 'label',
10642                    //cls : 'input-group-addon',
10643                     html : this.fieldLabel
10644
10645                 },
10646
10647                inputblock
10648
10649            ];
10650            
10651            if(this.indicatorpos == 'right'){
10652                 
10653                 cfg.cn = [
10654                     {
10655                         tag: 'label',
10656                        //cls : 'input-group-addon',
10657                         html : this.fieldLabel
10658
10659                     },
10660                     {
10661                         tag : 'i',
10662                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10663                         tooltip : 'This field is required'
10664                     },
10665
10666                    inputblock
10667
10668                ];
10669
10670             }
10671
10672         } else {
10673             
10674             cfg.cn = [
10675
10676                     inputblock
10677
10678             ];
10679                 
10680                 
10681         };
10682         
10683         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10684            cfg.cls += ' navbar-form';
10685         }
10686         
10687         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10688             // on BS4 we do this only if not form 
10689             cfg.cls += ' navbar-form';
10690             cfg.tag = 'li';
10691         }
10692         
10693         return cfg;
10694         
10695     },
10696     /**
10697      * return the real input element.
10698      */
10699     inputEl: function ()
10700     {
10701         return this.el.select('input.form-control',true).first();
10702     },
10703     
10704     tooltipEl : function()
10705     {
10706         return this.inputEl();
10707     },
10708     
10709     indicatorEl : function()
10710     {
10711         if (Roo.bootstrap.version == 4) {
10712             return false; // not enabled in v4 yet.
10713         }
10714         
10715         var indicator = this.el.select('i.roo-required-indicator',true).first();
10716         
10717         if(!indicator){
10718             return false;
10719         }
10720         
10721         return indicator;
10722         
10723     },
10724     
10725     setDisabled : function(v)
10726     {
10727         var i  = this.inputEl().dom;
10728         if (!v) {
10729             i.removeAttribute('disabled');
10730             return;
10731             
10732         }
10733         i.setAttribute('disabled','true');
10734     },
10735     initEvents : function()
10736     {
10737           
10738         this.inputEl().on("keydown" , this.fireKey,  this);
10739         this.inputEl().on("focus", this.onFocus,  this);
10740         this.inputEl().on("blur", this.onBlur,  this);
10741         
10742         this.inputEl().relayEvent('keyup', this);
10743         
10744         this.indicator = this.indicatorEl();
10745         
10746         if(this.indicator){
10747             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
10748         }
10749  
10750         // reference to original value for reset
10751         this.originalValue = this.getValue();
10752         //Roo.form.TextField.superclass.initEvents.call(this);
10753         if(this.validationEvent == 'keyup'){
10754             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10755             this.inputEl().on('keyup', this.filterValidation, this);
10756         }
10757         else if(this.validationEvent !== false){
10758             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10759         }
10760         
10761         if(this.selectOnFocus){
10762             this.on("focus", this.preFocus, this);
10763             
10764         }
10765         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10766             this.inputEl().on("keypress", this.filterKeys, this);
10767         } else {
10768             this.inputEl().relayEvent('keypress', this);
10769         }
10770        /* if(this.grow){
10771             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
10772             this.el.on("click", this.autoSize,  this);
10773         }
10774         */
10775         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10776             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10777         }
10778         
10779         if (typeof(this.before) == 'object') {
10780             this.before.render(this.el.select('.roo-input-before',true).first());
10781         }
10782         if (typeof(this.after) == 'object') {
10783             this.after.render(this.el.select('.roo-input-after',true).first());
10784         }
10785         
10786         this.inputEl().on('change', this.onChange, this);
10787         
10788     },
10789     filterValidation : function(e){
10790         if(!e.isNavKeyPress()){
10791             this.validationTask.delay(this.validationDelay);
10792         }
10793     },
10794      /**
10795      * Validates the field value
10796      * @return {Boolean} True if the value is valid, else false
10797      */
10798     validate : function(){
10799         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10800         if(this.disabled || this.validateValue(this.getRawValue())){
10801             this.markValid();
10802             return true;
10803         }
10804         
10805         this.markInvalid();
10806         return false;
10807     },
10808     
10809     
10810     /**
10811      * Validates a value according to the field's validation rules and marks the field as invalid
10812      * if the validation fails
10813      * @param {Mixed} value The value to validate
10814      * @return {Boolean} True if the value is valid, else false
10815      */
10816     validateValue : function(value)
10817     {
10818         if(this.getVisibilityEl().hasClass('hidden')){
10819             return true;
10820         }
10821         
10822         if(value.length < 1)  { // if it's blank
10823             if(this.allowBlank){
10824                 return true;
10825             }
10826             return false;
10827         }
10828         
10829         if(value.length < this.minLength){
10830             return false;
10831         }
10832         if(value.length > this.maxLength){
10833             return false;
10834         }
10835         if(this.vtype){
10836             var vt = Roo.form.VTypes;
10837             if(!vt[this.vtype](value, this)){
10838                 return false;
10839             }
10840         }
10841         if(typeof this.validator == "function"){
10842             var msg = this.validator(value);
10843             if(msg !== true){
10844                 return false;
10845             }
10846             if (typeof(msg) == 'string') {
10847                 this.invalidText = msg;
10848             }
10849         }
10850         
10851         if(this.regex && !this.regex.test(value)){
10852             return false;
10853         }
10854         
10855         return true;
10856     },
10857     
10858      // private
10859     fireKey : function(e){
10860         //Roo.log('field ' + e.getKey());
10861         if(e.isNavKeyPress()){
10862             this.fireEvent("specialkey", this, e);
10863         }
10864     },
10865     focus : function (selectText){
10866         if(this.rendered){
10867             this.inputEl().focus();
10868             if(selectText === true){
10869                 this.inputEl().dom.select();
10870             }
10871         }
10872         return this;
10873     } ,
10874     
10875     onFocus : function(){
10876         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10877            // this.el.addClass(this.focusClass);
10878         }
10879         if(!this.hasFocus){
10880             this.hasFocus = true;
10881             this.startValue = this.getValue();
10882             this.fireEvent("focus", this);
10883         }
10884     },
10885     
10886     beforeBlur : Roo.emptyFn,
10887
10888     
10889     // private
10890     onBlur : function(){
10891         this.beforeBlur();
10892         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10893             //this.el.removeClass(this.focusClass);
10894         }
10895         this.hasFocus = false;
10896         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
10897             this.validate();
10898         }
10899         var v = this.getValue();
10900         if(String(v) !== String(this.startValue)){
10901             this.fireEvent('change', this, v, this.startValue);
10902         }
10903         this.fireEvent("blur", this);
10904     },
10905     
10906     onChange : function(e)
10907     {
10908         var v = this.getValue();
10909         if(String(v) !== String(this.startValue)){
10910             this.fireEvent('change', this, v, this.startValue);
10911         }
10912         
10913     },
10914     
10915     /**
10916      * Resets the current field value to the originally loaded value and clears any validation messages
10917      */
10918     reset : function(){
10919         this.setValue(this.originalValue);
10920         this.validate();
10921     },
10922      /**
10923      * Returns the name of the field
10924      * @return {Mixed} name The name field
10925      */
10926     getName: function(){
10927         return this.name;
10928     },
10929      /**
10930      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
10931      * @return {Mixed} value The field value
10932      */
10933     getValue : function(){
10934         
10935         var v = this.inputEl().getValue();
10936         
10937         return v;
10938     },
10939     /**
10940      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
10941      * @return {Mixed} value The field value
10942      */
10943     getRawValue : function(){
10944         var v = this.inputEl().getValue();
10945         
10946         return v;
10947     },
10948     
10949     /**
10950      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
10951      * @param {Mixed} value The value to set
10952      */
10953     setRawValue : function(v){
10954         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10955     },
10956     
10957     selectText : function(start, end){
10958         var v = this.getRawValue();
10959         if(v.length > 0){
10960             start = start === undefined ? 0 : start;
10961             end = end === undefined ? v.length : end;
10962             var d = this.inputEl().dom;
10963             if(d.setSelectionRange){
10964                 d.setSelectionRange(start, end);
10965             }else if(d.createTextRange){
10966                 var range = d.createTextRange();
10967                 range.moveStart("character", start);
10968                 range.moveEnd("character", v.length-end);
10969                 range.select();
10970             }
10971         }
10972     },
10973     
10974     /**
10975      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
10976      * @param {Mixed} value The value to set
10977      */
10978     setValue : function(v){
10979         this.value = v;
10980         if(this.rendered){
10981             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10982             this.validate();
10983         }
10984     },
10985     
10986     /*
10987     processValue : function(value){
10988         if(this.stripCharsRe){
10989             var newValue = value.replace(this.stripCharsRe, '');
10990             if(newValue !== value){
10991                 this.setRawValue(newValue);
10992                 return newValue;
10993             }
10994         }
10995         return value;
10996     },
10997   */
10998     preFocus : function(){
10999         
11000         if(this.selectOnFocus){
11001             this.inputEl().dom.select();
11002         }
11003     },
11004     filterKeys : function(e){
11005         var k = e.getKey();
11006         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11007             return;
11008         }
11009         var c = e.getCharCode(), cc = String.fromCharCode(c);
11010         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11011             return;
11012         }
11013         if(!this.maskRe.test(cc)){
11014             e.stopEvent();
11015         }
11016     },
11017      /**
11018      * Clear any invalid styles/messages for this field
11019      */
11020     clearInvalid : function(){
11021         
11022         if(!this.el || this.preventMark){ // not rendered
11023             return;
11024         }
11025         
11026         
11027         this.el.removeClass([this.invalidClass, 'is-invalid']);
11028         
11029         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11030             
11031             var feedback = this.el.select('.form-control-feedback', true).first();
11032             
11033             if(feedback){
11034                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11035             }
11036             
11037         }
11038         
11039         if(this.indicator){
11040             this.indicator.removeClass('visible');
11041             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11042         }
11043         
11044         this.fireEvent('valid', this);
11045     },
11046     
11047      /**
11048      * Mark this field as valid
11049      */
11050     markValid : function()
11051     {
11052         if(!this.el  || this.preventMark){ // not rendered...
11053             return;
11054         }
11055         
11056         this.el.removeClass([this.invalidClass, this.validClass]);
11057         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11058
11059         var feedback = this.el.select('.form-control-feedback', true).first();
11060             
11061         if(feedback){
11062             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11063         }
11064         
11065         if(this.indicator){
11066             this.indicator.removeClass('visible');
11067             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11068         }
11069         
11070         if(this.disabled){
11071             return;
11072         }
11073         
11074            
11075         if(this.allowBlank && !this.getRawValue().length){
11076             return;
11077         }
11078         if (Roo.bootstrap.version == 3) {
11079             this.el.addClass(this.validClass);
11080         } else {
11081             this.inputEl().addClass('is-valid');
11082         }
11083
11084         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11085             
11086             var feedback = this.el.select('.form-control-feedback', true).first();
11087             
11088             if(feedback){
11089                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11090                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11091             }
11092             
11093         }
11094         
11095         this.fireEvent('valid', this);
11096     },
11097     
11098      /**
11099      * Mark this field as invalid
11100      * @param {String} msg The validation message
11101      */
11102     markInvalid : function(msg)
11103     {
11104         if(!this.el  || this.preventMark){ // not rendered
11105             return;
11106         }
11107         
11108         this.el.removeClass([this.invalidClass, this.validClass]);
11109         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11110         
11111         var feedback = this.el.select('.form-control-feedback', true).first();
11112             
11113         if(feedback){
11114             this.el.select('.form-control-feedback', true).first().removeClass(
11115                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11116         }
11117
11118         if(this.disabled){
11119             return;
11120         }
11121         
11122         if(this.allowBlank && !this.getRawValue().length){
11123             return;
11124         }
11125         
11126         if(this.indicator){
11127             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11128             this.indicator.addClass('visible');
11129         }
11130         if (Roo.bootstrap.version == 3) {
11131             this.el.addClass(this.invalidClass);
11132         } else {
11133             this.inputEl().addClass('is-invalid');
11134         }
11135         
11136         
11137         
11138         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11139             
11140             var feedback = this.el.select('.form-control-feedback', true).first();
11141             
11142             if(feedback){
11143                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11144                 
11145                 if(this.getValue().length || this.forceFeedback){
11146                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11147                 }
11148                 
11149             }
11150             
11151         }
11152         
11153         this.fireEvent('invalid', this, msg);
11154     },
11155     // private
11156     SafariOnKeyDown : function(event)
11157     {
11158         // this is a workaround for a password hang bug on chrome/ webkit.
11159         if (this.inputEl().dom.type != 'password') {
11160             return;
11161         }
11162         
11163         var isSelectAll = false;
11164         
11165         if(this.inputEl().dom.selectionEnd > 0){
11166             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11167         }
11168         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11169             event.preventDefault();
11170             this.setValue('');
11171             return;
11172         }
11173         
11174         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11175             
11176             event.preventDefault();
11177             // this is very hacky as keydown always get's upper case.
11178             //
11179             var cc = String.fromCharCode(event.getCharCode());
11180             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11181             
11182         }
11183     },
11184     adjustWidth : function(tag, w){
11185         tag = tag.toLowerCase();
11186         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11187             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11188                 if(tag == 'input'){
11189                     return w + 2;
11190                 }
11191                 if(tag == 'textarea'){
11192                     return w-2;
11193                 }
11194             }else if(Roo.isOpera){
11195                 if(tag == 'input'){
11196                     return w + 2;
11197                 }
11198                 if(tag == 'textarea'){
11199                     return w-2;
11200                 }
11201             }
11202         }
11203         return w;
11204     },
11205     
11206     setFieldLabel : function(v)
11207     {
11208         if(!this.rendered){
11209             return;
11210         }
11211         
11212         if(this.indicatorEl()){
11213             var ar = this.el.select('label > span',true);
11214             
11215             if (ar.elements.length) {
11216                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11217                 this.fieldLabel = v;
11218                 return;
11219             }
11220             
11221             var br = this.el.select('label',true);
11222             
11223             if(br.elements.length) {
11224                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11225                 this.fieldLabel = v;
11226                 return;
11227             }
11228             
11229             Roo.log('Cannot Found any of label > span || label in input');
11230             return;
11231         }
11232         
11233         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11234         this.fieldLabel = v;
11235         
11236         
11237     }
11238 });
11239
11240  
11241 /*
11242  * - LGPL
11243  *
11244  * Input
11245  * 
11246  */
11247
11248 /**
11249  * @class Roo.bootstrap.TextArea
11250  * @extends Roo.bootstrap.Input
11251  * Bootstrap TextArea class
11252  * @cfg {Number} cols Specifies the visible width of a text area
11253  * @cfg {Number} rows Specifies the visible number of lines in a text area
11254  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11255  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11256  * @cfg {string} html text
11257  * 
11258  * @constructor
11259  * Create a new TextArea
11260  * @param {Object} config The config object
11261  */
11262
11263 Roo.bootstrap.TextArea = function(config){
11264     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11265    
11266 };
11267
11268 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11269      
11270     cols : false,
11271     rows : 5,
11272     readOnly : false,
11273     warp : 'soft',
11274     resize : false,
11275     value: false,
11276     html: false,
11277     
11278     getAutoCreate : function(){
11279         
11280         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11281         
11282         var id = Roo.id();
11283         
11284         var cfg = {};
11285         
11286         if(this.inputType != 'hidden'){
11287             cfg.cls = 'form-group' //input-group
11288         }
11289         
11290         var input =  {
11291             tag: 'textarea',
11292             id : id,
11293             warp : this.warp,
11294             rows : this.rows,
11295             value : this.value || '',
11296             html: this.html || '',
11297             cls : 'form-control',
11298             placeholder : this.placeholder || '' 
11299             
11300         };
11301         
11302         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11303             input.maxLength = this.maxLength;
11304         }
11305         
11306         if(this.resize){
11307             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11308         }
11309         
11310         if(this.cols){
11311             input.cols = this.cols;
11312         }
11313         
11314         if (this.readOnly) {
11315             input.readonly = true;
11316         }
11317         
11318         if (this.name) {
11319             input.name = this.name;
11320         }
11321         
11322         if (this.size) {
11323             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11324         }
11325         
11326         var settings=this;
11327         ['xs','sm','md','lg'].map(function(size){
11328             if (settings[size]) {
11329                 cfg.cls += ' col-' + size + '-' + settings[size];
11330             }
11331         });
11332         
11333         var inputblock = input;
11334         
11335         if(this.hasFeedback && !this.allowBlank){
11336             
11337             var feedback = {
11338                 tag: 'span',
11339                 cls: 'glyphicon form-control-feedback'
11340             };
11341
11342             inputblock = {
11343                 cls : 'has-feedback',
11344                 cn :  [
11345                     input,
11346                     feedback
11347                 ] 
11348             };  
11349         }
11350         
11351         
11352         if (this.before || this.after) {
11353             
11354             inputblock = {
11355                 cls : 'input-group',
11356                 cn :  [] 
11357             };
11358             if (this.before) {
11359                 inputblock.cn.push({
11360                     tag :'span',
11361                     cls : 'input-group-addon',
11362                     html : this.before
11363                 });
11364             }
11365             
11366             inputblock.cn.push(input);
11367             
11368             if(this.hasFeedback && !this.allowBlank){
11369                 inputblock.cls += ' has-feedback';
11370                 inputblock.cn.push(feedback);
11371             }
11372             
11373             if (this.after) {
11374                 inputblock.cn.push({
11375                     tag :'span',
11376                     cls : 'input-group-addon',
11377                     html : this.after
11378                 });
11379             }
11380             
11381         }
11382         
11383         if (align ==='left' && this.fieldLabel.length) {
11384             cfg.cn = [
11385                 {
11386                     tag: 'label',
11387                     'for' :  id,
11388                     cls : 'control-label',
11389                     html : this.fieldLabel
11390                 },
11391                 {
11392                     cls : "",
11393                     cn: [
11394                         inputblock
11395                     ]
11396                 }
11397
11398             ];
11399             
11400             if(this.labelWidth > 12){
11401                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11402             }
11403
11404             if(this.labelWidth < 13 && this.labelmd == 0){
11405                 this.labelmd = this.labelWidth;
11406             }
11407
11408             if(this.labellg > 0){
11409                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11410                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11411             }
11412
11413             if(this.labelmd > 0){
11414                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11415                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11416             }
11417
11418             if(this.labelsm > 0){
11419                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11420                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11421             }
11422
11423             if(this.labelxs > 0){
11424                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11425                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11426             }
11427             
11428         } else if ( this.fieldLabel.length) {
11429             cfg.cn = [
11430
11431                {
11432                    tag: 'label',
11433                    //cls : 'input-group-addon',
11434                    html : this.fieldLabel
11435
11436                },
11437
11438                inputblock
11439
11440            ];
11441
11442         } else {
11443
11444             cfg.cn = [
11445
11446                 inputblock
11447
11448             ];
11449                 
11450         }
11451         
11452         if (this.disabled) {
11453             input.disabled=true;
11454         }
11455         
11456         return cfg;
11457         
11458     },
11459     /**
11460      * return the real textarea element.
11461      */
11462     inputEl: function ()
11463     {
11464         return this.el.select('textarea.form-control',true).first();
11465     },
11466     
11467     /**
11468      * Clear any invalid styles/messages for this field
11469      */
11470     clearInvalid : function()
11471     {
11472         
11473         if(!this.el || this.preventMark){ // not rendered
11474             return;
11475         }
11476         
11477         var label = this.el.select('label', true).first();
11478         var icon = this.el.select('i.fa-star', true).first();
11479         
11480         if(label && icon){
11481             icon.remove();
11482         }
11483         this.el.removeClass( this.validClass);
11484         this.inputEl().removeClass('is-invalid');
11485          
11486         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11487             
11488             var feedback = this.el.select('.form-control-feedback', true).first();
11489             
11490             if(feedback){
11491                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11492             }
11493             
11494         }
11495         
11496         this.fireEvent('valid', this);
11497     },
11498     
11499      /**
11500      * Mark this field as valid
11501      */
11502     markValid : function()
11503     {
11504         if(!this.el  || this.preventMark){ // not rendered
11505             return;
11506         }
11507         
11508         this.el.removeClass([this.invalidClass, this.validClass]);
11509         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11510         
11511         var feedback = this.el.select('.form-control-feedback', true).first();
11512             
11513         if(feedback){
11514             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11515         }
11516
11517         if(this.disabled || this.allowBlank){
11518             return;
11519         }
11520         
11521         var label = this.el.select('label', true).first();
11522         var icon = this.el.select('i.fa-star', true).first();
11523         
11524         if(label && icon){
11525             icon.remove();
11526         }
11527         if (Roo.bootstrap.version == 3) {
11528             this.el.addClass(this.validClass);
11529         } else {
11530             this.inputEl().addClass('is-valid');
11531         }
11532         
11533         
11534         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11535             
11536             var feedback = this.el.select('.form-control-feedback', true).first();
11537             
11538             if(feedback){
11539                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11540                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11541             }
11542             
11543         }
11544         
11545         this.fireEvent('valid', this);
11546     },
11547     
11548      /**
11549      * Mark this field as invalid
11550      * @param {String} msg The validation message
11551      */
11552     markInvalid : function(msg)
11553     {
11554         if(!this.el  || this.preventMark){ // not rendered
11555             return;
11556         }
11557         
11558         this.el.removeClass([this.invalidClass, this.validClass]);
11559         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11560         
11561         var feedback = this.el.select('.form-control-feedback', true).first();
11562             
11563         if(feedback){
11564             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11565         }
11566
11567         if(this.disabled || this.allowBlank){
11568             return;
11569         }
11570         
11571         var label = this.el.select('label', true).first();
11572         var icon = this.el.select('i.fa-star', true).first();
11573         
11574         if(!this.getValue().length && label && !icon){
11575             this.el.createChild({
11576                 tag : 'i',
11577                 cls : 'text-danger fa fa-lg fa-star',
11578                 tooltip : 'This field is required',
11579                 style : 'margin-right:5px;'
11580             }, label, true);
11581         }
11582         
11583         if (Roo.bootstrap.version == 3) {
11584             this.el.addClass(this.invalidClass);
11585         } else {
11586             this.inputEl().addClass('is-invalid');
11587         }
11588         
11589         // fixme ... this may be depricated need to test..
11590         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11591             
11592             var feedback = this.el.select('.form-control-feedback', true).first();
11593             
11594             if(feedback){
11595                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11596                 
11597                 if(this.getValue().length || this.forceFeedback){
11598                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11599                 }
11600                 
11601             }
11602             
11603         }
11604         
11605         this.fireEvent('invalid', this, msg);
11606     }
11607 });
11608
11609  
11610 /*
11611  * - LGPL
11612  *
11613  * trigger field - base class for combo..
11614  * 
11615  */
11616  
11617 /**
11618  * @class Roo.bootstrap.TriggerField
11619  * @extends Roo.bootstrap.Input
11620  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11621  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11622  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11623  * for which you can provide a custom implementation.  For example:
11624  * <pre><code>
11625 var trigger = new Roo.bootstrap.TriggerField();
11626 trigger.onTriggerClick = myTriggerFn;
11627 trigger.applyTo('my-field');
11628 </code></pre>
11629  *
11630  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11631  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11632  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11633  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11634  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11635
11636  * @constructor
11637  * Create a new TriggerField.
11638  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11639  * to the base TextField)
11640  */
11641 Roo.bootstrap.TriggerField = function(config){
11642     this.mimicing = false;
11643     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11644 };
11645
11646 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11647     /**
11648      * @cfg {String} triggerClass A CSS class to apply to the trigger
11649      */
11650      /**
11651      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11652      */
11653     hideTrigger:false,
11654
11655     /**
11656      * @cfg {Boolean} removable (true|false) special filter default false
11657      */
11658     removable : false,
11659     
11660     /** @cfg {Boolean} grow @hide */
11661     /** @cfg {Number} growMin @hide */
11662     /** @cfg {Number} growMax @hide */
11663
11664     /**
11665      * @hide 
11666      * @method
11667      */
11668     autoSize: Roo.emptyFn,
11669     // private
11670     monitorTab : true,
11671     // private
11672     deferHeight : true,
11673
11674     
11675     actionMode : 'wrap',
11676     
11677     caret : false,
11678     
11679     
11680     getAutoCreate : function(){
11681        
11682         var align = this.labelAlign || this.parentLabelAlign();
11683         
11684         var id = Roo.id();
11685         
11686         var cfg = {
11687             cls: 'form-group' //input-group
11688         };
11689         
11690         
11691         var input =  {
11692             tag: 'input',
11693             id : id,
11694             type : this.inputType,
11695             cls : 'form-control',
11696             autocomplete: 'new-password',
11697             placeholder : this.placeholder || '' 
11698             
11699         };
11700         if (this.name) {
11701             input.name = this.name;
11702         }
11703         if (this.size) {
11704             input.cls += ' input-' + this.size;
11705         }
11706         
11707         if (this.disabled) {
11708             input.disabled=true;
11709         }
11710         
11711         var inputblock = input;
11712         
11713         if(this.hasFeedback && !this.allowBlank){
11714             
11715             var feedback = {
11716                 tag: 'span',
11717                 cls: 'glyphicon form-control-feedback'
11718             };
11719             
11720             if(this.removable && !this.editable  ){
11721                 inputblock = {
11722                     cls : 'has-feedback',
11723                     cn :  [
11724                         inputblock,
11725                         {
11726                             tag: 'button',
11727                             html : 'x',
11728                             cls : 'roo-combo-removable-btn close'
11729                         },
11730                         feedback
11731                     ] 
11732                 };
11733             } else {
11734                 inputblock = {
11735                     cls : 'has-feedback',
11736                     cn :  [
11737                         inputblock,
11738                         feedback
11739                     ] 
11740                 };
11741             }
11742
11743         } else {
11744             if(this.removable && !this.editable ){
11745                 inputblock = {
11746                     cls : 'roo-removable',
11747                     cn :  [
11748                         inputblock,
11749                         {
11750                             tag: 'button',
11751                             html : 'x',
11752                             cls : 'roo-combo-removable-btn close'
11753                         }
11754                     ] 
11755                 };
11756             }
11757         }
11758         
11759         if (this.before || this.after) {
11760             
11761             inputblock = {
11762                 cls : 'input-group',
11763                 cn :  [] 
11764             };
11765             if (this.before) {
11766                 inputblock.cn.push({
11767                     tag :'span',
11768                     cls : 'input-group-addon input-group-prepend input-group-text',
11769                     html : this.before
11770                 });
11771             }
11772             
11773             inputblock.cn.push(input);
11774             
11775             if(this.hasFeedback && !this.allowBlank){
11776                 inputblock.cls += ' has-feedback';
11777                 inputblock.cn.push(feedback);
11778             }
11779             
11780             if (this.after) {
11781                 inputblock.cn.push({
11782                     tag :'span',
11783                     cls : 'input-group-addon input-group-append input-group-text',
11784                     html : this.after
11785                 });
11786             }
11787             
11788         };
11789         
11790       
11791         
11792         var ibwrap = inputblock;
11793         
11794         if(this.multiple){
11795             ibwrap = {
11796                 tag: 'ul',
11797                 cls: 'roo-select2-choices',
11798                 cn:[
11799                     {
11800                         tag: 'li',
11801                         cls: 'roo-select2-search-field',
11802                         cn: [
11803
11804                             inputblock
11805                         ]
11806                     }
11807                 ]
11808             };
11809                 
11810         }
11811         
11812         var combobox = {
11813             cls: 'roo-select2-container input-group',
11814             cn: [
11815                  {
11816                     tag: 'input',
11817                     type : 'hidden',
11818                     cls: 'form-hidden-field'
11819                 },
11820                 ibwrap
11821             ]
11822         };
11823         
11824         if(!this.multiple && this.showToggleBtn){
11825             
11826             var caret = {
11827                         tag: 'span',
11828                         cls: 'caret'
11829              };
11830             if (this.caret != false) {
11831                 caret = {
11832                      tag: 'i',
11833                      cls: 'fa fa-' + this.caret
11834                 };
11835                 
11836             }
11837             
11838             combobox.cn.push({
11839                 tag :'span',
11840                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11841                 cn : [
11842                     Roo.bootstrap.version == 3 ? caret : '',
11843                     {
11844                         tag: 'span',
11845                         cls: 'combobox-clear',
11846                         cn  : [
11847                             {
11848                                 tag : 'i',
11849                                 cls: 'icon-remove'
11850                             }
11851                         ]
11852                     }
11853                 ]
11854
11855             })
11856         }
11857         
11858         if(this.multiple){
11859             combobox.cls += ' roo-select2-container-multi';
11860         }
11861          var indicator = {
11862             tag : 'i',
11863             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11864             tooltip : 'This field is required'
11865         };
11866         if (Roo.bootstrap.version == 4) {
11867             indicator = {
11868                 tag : 'i',
11869                 style : 'display:none'
11870             };
11871         }
11872         
11873         
11874         if (align ==='left' && this.fieldLabel.length) {
11875             
11876             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11877
11878             cfg.cn = [
11879                 indicator,
11880                 {
11881                     tag: 'label',
11882                     'for' :  id,
11883                     cls : 'control-label',
11884                     html : this.fieldLabel
11885
11886                 },
11887                 {
11888                     cls : "", 
11889                     cn: [
11890                         combobox
11891                     ]
11892                 }
11893
11894             ];
11895             
11896             var labelCfg = cfg.cn[1];
11897             var contentCfg = cfg.cn[2];
11898             
11899             if(this.indicatorpos == 'right'){
11900                 cfg.cn = [
11901                     {
11902                         tag: 'label',
11903                         'for' :  id,
11904                         cls : 'control-label',
11905                         cn : [
11906                             {
11907                                 tag : 'span',
11908                                 html : this.fieldLabel
11909                             },
11910                             indicator
11911                         ]
11912                     },
11913                     {
11914                         cls : "", 
11915                         cn: [
11916                             combobox
11917                         ]
11918                     }
11919
11920                 ];
11921                 
11922                 labelCfg = cfg.cn[0];
11923                 contentCfg = cfg.cn[1];
11924             }
11925             
11926             if(this.labelWidth > 12){
11927                 labelCfg.style = "width: " + this.labelWidth + 'px';
11928             }
11929             
11930             if(this.labelWidth < 13 && this.labelmd == 0){
11931                 this.labelmd = this.labelWidth;
11932             }
11933             
11934             if(this.labellg > 0){
11935                 labelCfg.cls += ' col-lg-' + this.labellg;
11936                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11937             }
11938             
11939             if(this.labelmd > 0){
11940                 labelCfg.cls += ' col-md-' + this.labelmd;
11941                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11942             }
11943             
11944             if(this.labelsm > 0){
11945                 labelCfg.cls += ' col-sm-' + this.labelsm;
11946                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11947             }
11948             
11949             if(this.labelxs > 0){
11950                 labelCfg.cls += ' col-xs-' + this.labelxs;
11951                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11952             }
11953             
11954         } else if ( this.fieldLabel.length) {
11955 //                Roo.log(" label");
11956             cfg.cn = [
11957                 indicator,
11958                {
11959                    tag: 'label',
11960                    //cls : 'input-group-addon',
11961                    html : this.fieldLabel
11962
11963                },
11964
11965                combobox
11966
11967             ];
11968             
11969             if(this.indicatorpos == 'right'){
11970                 
11971                 cfg.cn = [
11972                     {
11973                        tag: 'label',
11974                        cn : [
11975                            {
11976                                tag : 'span',
11977                                html : this.fieldLabel
11978                            },
11979                            indicator
11980                        ]
11981
11982                     },
11983                     combobox
11984
11985                 ];
11986
11987             }
11988
11989         } else {
11990             
11991 //                Roo.log(" no label && no align");
11992                 cfg = combobox
11993                      
11994                 
11995         }
11996         
11997         var settings=this;
11998         ['xs','sm','md','lg'].map(function(size){
11999             if (settings[size]) {
12000                 cfg.cls += ' col-' + size + '-' + settings[size];
12001             }
12002         });
12003         
12004         return cfg;
12005         
12006     },
12007     
12008     
12009     
12010     // private
12011     onResize : function(w, h){
12012 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12013 //        if(typeof w == 'number'){
12014 //            var x = w - this.trigger.getWidth();
12015 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12016 //            this.trigger.setStyle('left', x+'px');
12017 //        }
12018     },
12019
12020     // private
12021     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12022
12023     // private
12024     getResizeEl : function(){
12025         return this.inputEl();
12026     },
12027
12028     // private
12029     getPositionEl : function(){
12030         return this.inputEl();
12031     },
12032
12033     // private
12034     alignErrorIcon : function(){
12035         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12036     },
12037
12038     // private
12039     initEvents : function(){
12040         
12041         this.createList();
12042         
12043         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12044         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12045         if(!this.multiple && this.showToggleBtn){
12046             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12047             if(this.hideTrigger){
12048                 this.trigger.setDisplayed(false);
12049             }
12050             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12051         }
12052         
12053         if(this.multiple){
12054             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12055         }
12056         
12057         if(this.removable && !this.editable && !this.tickable){
12058             var close = this.closeTriggerEl();
12059             
12060             if(close){
12061                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12062                 close.on('click', this.removeBtnClick, this, close);
12063             }
12064         }
12065         
12066         //this.trigger.addClassOnOver('x-form-trigger-over');
12067         //this.trigger.addClassOnClick('x-form-trigger-click');
12068         
12069         //if(!this.width){
12070         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12071         //}
12072     },
12073     
12074     closeTriggerEl : function()
12075     {
12076         var close = this.el.select('.roo-combo-removable-btn', true).first();
12077         return close ? close : false;
12078     },
12079     
12080     removeBtnClick : function(e, h, el)
12081     {
12082         e.preventDefault();
12083         
12084         if(this.fireEvent("remove", this) !== false){
12085             this.reset();
12086             this.fireEvent("afterremove", this)
12087         }
12088     },
12089     
12090     createList : function()
12091     {
12092         this.list = Roo.get(document.body).createChild({
12093             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12094             cls: 'typeahead typeahead-long dropdown-menu',
12095             style: 'display:none'
12096         });
12097         
12098         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12099         
12100     },
12101
12102     // private
12103     initTrigger : function(){
12104        
12105     },
12106
12107     // private
12108     onDestroy : function(){
12109         if(this.trigger){
12110             this.trigger.removeAllListeners();
12111           //  this.trigger.remove();
12112         }
12113         //if(this.wrap){
12114         //    this.wrap.remove();
12115         //}
12116         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12117     },
12118
12119     // private
12120     onFocus : function(){
12121         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12122         /*
12123         if(!this.mimicing){
12124             this.wrap.addClass('x-trigger-wrap-focus');
12125             this.mimicing = true;
12126             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12127             if(this.monitorTab){
12128                 this.el.on("keydown", this.checkTab, this);
12129             }
12130         }
12131         */
12132     },
12133
12134     // private
12135     checkTab : function(e){
12136         if(e.getKey() == e.TAB){
12137             this.triggerBlur();
12138         }
12139     },
12140
12141     // private
12142     onBlur : function(){
12143         // do nothing
12144     },
12145
12146     // private
12147     mimicBlur : function(e, t){
12148         /*
12149         if(!this.wrap.contains(t) && this.validateBlur()){
12150             this.triggerBlur();
12151         }
12152         */
12153     },
12154
12155     // private
12156     triggerBlur : function(){
12157         this.mimicing = false;
12158         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12159         if(this.monitorTab){
12160             this.el.un("keydown", this.checkTab, this);
12161         }
12162         //this.wrap.removeClass('x-trigger-wrap-focus');
12163         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12164     },
12165
12166     // private
12167     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12168     validateBlur : function(e, t){
12169         return true;
12170     },
12171
12172     // private
12173     onDisable : function(){
12174         this.inputEl().dom.disabled = true;
12175         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12176         //if(this.wrap){
12177         //    this.wrap.addClass('x-item-disabled');
12178         //}
12179     },
12180
12181     // private
12182     onEnable : function(){
12183         this.inputEl().dom.disabled = false;
12184         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12185         //if(this.wrap){
12186         //    this.el.removeClass('x-item-disabled');
12187         //}
12188     },
12189
12190     // private
12191     onShow : function(){
12192         var ae = this.getActionEl();
12193         
12194         if(ae){
12195             ae.dom.style.display = '';
12196             ae.dom.style.visibility = 'visible';
12197         }
12198     },
12199
12200     // private
12201     
12202     onHide : function(){
12203         var ae = this.getActionEl();
12204         ae.dom.style.display = 'none';
12205     },
12206
12207     /**
12208      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12209      * by an implementing function.
12210      * @method
12211      * @param {EventObject} e
12212      */
12213     onTriggerClick : Roo.emptyFn
12214 });
12215  
12216 /*
12217 * Licence: LGPL
12218 */
12219
12220 /**
12221  * @class Roo.bootstrap.CardUploader
12222  * @extends Roo.bootstrap.Button
12223  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12224  * @cfg {Number} errorTimeout default 3000
12225  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12226  * @cfg {Array}  html The button text.
12227
12228  *
12229  * @constructor
12230  * Create a new CardUploader
12231  * @param {Object} config The config object
12232  */
12233
12234 Roo.bootstrap.CardUploader = function(config){
12235     
12236  
12237     
12238     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12239     
12240     
12241     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12242         return r.data.id
12243         });
12244     
12245     
12246 };
12247
12248 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12249     
12250      
12251     errorTimeout : 3000,
12252      
12253     images : false,
12254    
12255     fileCollection : false,
12256     allowBlank : true,
12257     
12258     getAutoCreate : function()
12259     {
12260         
12261         var cfg =  {
12262             cls :'form-group' ,
12263             cn : [
12264                
12265                 {
12266                     tag: 'label',
12267                    //cls : 'input-group-addon',
12268                     html : this.fieldLabel
12269
12270                 },
12271
12272                 {
12273                     tag: 'input',
12274                     type : 'hidden',
12275                     value : this.value,
12276                     cls : 'd-none  form-control'
12277                 },
12278                 
12279                 {
12280                     tag: 'input',
12281                     multiple : 'multiple',
12282                     type : 'file',
12283                     cls : 'd-none  roo-card-upload-selector'
12284                 },
12285                 
12286                 {
12287                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12288                 },
12289                 {
12290                     cls : 'card-columns roo-card-uploader-container'
12291                 }
12292
12293             ]
12294         };
12295            
12296          
12297         return cfg;
12298     },
12299     
12300     getChildContainer : function() /// what children are added to.
12301     {
12302         return this.containerEl;
12303     },
12304    
12305     getButtonContainer : function() /// what children are added to.
12306     {
12307         return this.el.select(".roo-card-uploader-button-container").first();
12308     },
12309    
12310     initEvents : function()
12311     {
12312         
12313         Roo.bootstrap.Input.prototype.initEvents.call(this);
12314         
12315         var t = this;
12316         this.addxtype({
12317             xns: Roo.bootstrap,
12318
12319             xtype : 'Button',
12320             container_method : 'getButtonContainer' ,            
12321             html :  this.html, // fix changable?
12322             cls : 'w-100 ',
12323             listeners : {
12324                 'click' : function(btn, e) {
12325                     t.onClick(e);
12326                 }
12327             }
12328         });
12329         
12330         
12331         
12332         
12333         this.urlAPI = (window.createObjectURL && window) || 
12334                                 (window.URL && URL.revokeObjectURL && URL) || 
12335                                 (window.webkitURL && webkitURL);
12336                         
12337          
12338          
12339          
12340         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12341         
12342         this.selectorEl.on('change', this.onFileSelected, this);
12343         if (this.images) {
12344             var t = this;
12345             this.images.forEach(function(img) {
12346                 t.addCard(img)
12347             });
12348             this.images = false;
12349         }
12350         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12351          
12352        
12353     },
12354     
12355    
12356     onClick : function(e)
12357     {
12358         e.preventDefault();
12359          
12360         this.selectorEl.dom.click();
12361          
12362     },
12363     
12364     onFileSelected : function(e)
12365     {
12366         e.preventDefault();
12367         
12368         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12369             return;
12370         }
12371         
12372         Roo.each(this.selectorEl.dom.files, function(file){    
12373             this.addFile(file);
12374         }, this);
12375          
12376     },
12377     
12378       
12379     
12380       
12381     
12382     addFile : function(file)
12383     {
12384            
12385         if(typeof(file) === 'string'){
12386             throw "Add file by name?"; // should not happen
12387             return;
12388         }
12389         
12390         if(!file || !this.urlAPI){
12391             return;
12392         }
12393         
12394         // file;
12395         // file.type;
12396         
12397         var _this = this;
12398         
12399         
12400         var url = _this.urlAPI.createObjectURL( file);
12401            
12402         this.addCard({
12403             id : Roo.bootstrap.CardUploader.ID--,
12404             is_uploaded : false,
12405             src : url,
12406             title : file.name,
12407             mimetype : file.type,
12408             preview : false,
12409             is_deleted : 0
12410         })
12411         
12412     },
12413     
12414     addCard : function (data)
12415     {
12416         // hidden input element?
12417         // if the file is not an image...
12418         //then we need to use something other that and header_image
12419         var t = this;
12420         //   remove.....
12421         var footer = [
12422             {
12423                 xns : Roo.bootstrap,
12424                 xtype : 'CardFooter',
12425                 items: [
12426                     {
12427                         xns : Roo.bootstrap,
12428                         xtype : 'Element',
12429                         cls : 'd-flex',
12430                         items : [
12431                             
12432                             {
12433                                 xns : Roo.bootstrap,
12434                                 xtype : 'Button',
12435                                 html : String.format("<small>{0}</small>", data.title),
12436                                 cls : 'col-11 text-left',
12437                                 size: 'sm',
12438                                 weight: 'link',
12439                                 fa : 'download',
12440                                 listeners : {
12441                                     click : function() {
12442                                         this.downloadCard(data.id)
12443                                     }
12444                                 }
12445                             },
12446                           
12447                             {
12448                                 xns : Roo.bootstrap,
12449                                 xtype : 'Button',
12450                                 
12451                                 size : 'sm',
12452                                 weight: 'danger',
12453                                 cls : 'col-1',
12454                                 fa : 'times',
12455                                 listeners : {
12456                                     click : function() {
12457                                         t.removeCard(data.id)
12458                                     }
12459                                 }
12460                             }
12461                         ]
12462                     }
12463                     
12464                 ] 
12465             }
12466             
12467         ];
12468
12469         var cn = this.addxtype(
12470             {
12471                  
12472                 xns : Roo.bootstrap,
12473                 xtype : 'Card',
12474                 closeable : true,
12475                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12476                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12477                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12478                 data : data,
12479                 html : false,
12480                  
12481                 items : footer,
12482                 initEvents : function() {
12483                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12484                     this.imgEl = this.el.select('.card-img-top').first();
12485                     if (this.imgEl) {
12486                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12487                         this.imgEl.set({ 'pointer' : 'cursor' });
12488                                   
12489                     }
12490                     
12491                   
12492                 }
12493                 
12494             }
12495         );
12496         // dont' really need ot update items.
12497         // this.items.push(cn);
12498         this.fileCollection.add(cn);
12499         this.updateInput();
12500         
12501     },
12502     removeCard : function(id)
12503     {
12504         
12505         var card  = this.fileCollection.get(id);
12506         card.data.is_deleted = 1;
12507         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12508         this.fileCollection.remove(card);
12509         //this.items = this.items.filter(function(e) { return e != card });
12510         // dont' really need ot update items.
12511         card.el.dom.parentNode.removeChild(card.el.dom);
12512         
12513     },
12514     reset: function()
12515     {
12516         this.fileCollection.each(function(card) {
12517             card.el.dom.parentNode.removeChild(card.el.dom);    
12518         });
12519         this.fileCollection.clear();
12520         this.updateInput();
12521     },
12522     
12523     updateInput : function()
12524     {
12525         var data = [];
12526         this.fileCollection.each(function(e) {
12527             data.push(e.data);
12528         });
12529         
12530         this.inputEl().dom.value = JSON.stringify(data);
12531     }
12532     
12533     
12534 });
12535
12536
12537 Roo.bootstrap.CardUploader.ID = -1;/*
12538  * Based on:
12539  * Ext JS Library 1.1.1
12540  * Copyright(c) 2006-2007, Ext JS, LLC.
12541  *
12542  * Originally Released Under LGPL - original licence link has changed is not relivant.
12543  *
12544  * Fork - LGPL
12545  * <script type="text/javascript">
12546  */
12547
12548
12549 /**
12550  * @class Roo.data.SortTypes
12551  * @singleton
12552  * Defines the default sorting (casting?) comparison functions used when sorting data.
12553  */
12554 Roo.data.SortTypes = {
12555     /**
12556      * Default sort that does nothing
12557      * @param {Mixed} s The value being converted
12558      * @return {Mixed} The comparison value
12559      */
12560     none : function(s){
12561         return s;
12562     },
12563     
12564     /**
12565      * The regular expression used to strip tags
12566      * @type {RegExp}
12567      * @property
12568      */
12569     stripTagsRE : /<\/?[^>]+>/gi,
12570     
12571     /**
12572      * Strips all HTML tags to sort on text only
12573      * @param {Mixed} s The value being converted
12574      * @return {String} The comparison value
12575      */
12576     asText : function(s){
12577         return String(s).replace(this.stripTagsRE, "");
12578     },
12579     
12580     /**
12581      * Strips all HTML tags to sort on text only - Case insensitive
12582      * @param {Mixed} s The value being converted
12583      * @return {String} The comparison value
12584      */
12585     asUCText : function(s){
12586         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12587     },
12588     
12589     /**
12590      * Case insensitive string
12591      * @param {Mixed} s The value being converted
12592      * @return {String} The comparison value
12593      */
12594     asUCString : function(s) {
12595         return String(s).toUpperCase();
12596     },
12597     
12598     /**
12599      * Date sorting
12600      * @param {Mixed} s The value being converted
12601      * @return {Number} The comparison value
12602      */
12603     asDate : function(s) {
12604         if(!s){
12605             return 0;
12606         }
12607         if(s instanceof Date){
12608             return s.getTime();
12609         }
12610         return Date.parse(String(s));
12611     },
12612     
12613     /**
12614      * Float sorting
12615      * @param {Mixed} s The value being converted
12616      * @return {Float} The comparison value
12617      */
12618     asFloat : function(s) {
12619         var val = parseFloat(String(s).replace(/,/g, ""));
12620         if(isNaN(val)) {
12621             val = 0;
12622         }
12623         return val;
12624     },
12625     
12626     /**
12627      * Integer sorting
12628      * @param {Mixed} s The value being converted
12629      * @return {Number} The comparison value
12630      */
12631     asInt : function(s) {
12632         var val = parseInt(String(s).replace(/,/g, ""));
12633         if(isNaN(val)) {
12634             val = 0;
12635         }
12636         return val;
12637     }
12638 };/*
12639  * Based on:
12640  * Ext JS Library 1.1.1
12641  * Copyright(c) 2006-2007, Ext JS, LLC.
12642  *
12643  * Originally Released Under LGPL - original licence link has changed is not relivant.
12644  *
12645  * Fork - LGPL
12646  * <script type="text/javascript">
12647  */
12648
12649 /**
12650 * @class Roo.data.Record
12651  * Instances of this class encapsulate both record <em>definition</em> information, and record
12652  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12653  * to access Records cached in an {@link Roo.data.Store} object.<br>
12654  * <p>
12655  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12656  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12657  * objects.<br>
12658  * <p>
12659  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12660  * @constructor
12661  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12662  * {@link #create}. The parameters are the same.
12663  * @param {Array} data An associative Array of data values keyed by the field name.
12664  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12665  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12666  * not specified an integer id is generated.
12667  */
12668 Roo.data.Record = function(data, id){
12669     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12670     this.data = data;
12671 };
12672
12673 /**
12674  * Generate a constructor for a specific record layout.
12675  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12676  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12677  * Each field definition object may contain the following properties: <ul>
12678  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
12679  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12680  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12681  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12682  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12683  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12684  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12685  * this may be omitted.</p></li>
12686  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12687  * <ul><li>auto (Default, implies no conversion)</li>
12688  * <li>string</li>
12689  * <li>int</li>
12690  * <li>float</li>
12691  * <li>boolean</li>
12692  * <li>date</li></ul></p></li>
12693  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12694  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12695  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12696  * by the Reader into an object that will be stored in the Record. It is passed the
12697  * following parameters:<ul>
12698  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12699  * </ul></p></li>
12700  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12701  * </ul>
12702  * <br>usage:<br><pre><code>
12703 var TopicRecord = Roo.data.Record.create(
12704     {name: 'title', mapping: 'topic_title'},
12705     {name: 'author', mapping: 'username'},
12706     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12707     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12708     {name: 'lastPoster', mapping: 'user2'},
12709     {name: 'excerpt', mapping: 'post_text'}
12710 );
12711
12712 var myNewRecord = new TopicRecord({
12713     title: 'Do my job please',
12714     author: 'noobie',
12715     totalPosts: 1,
12716     lastPost: new Date(),
12717     lastPoster: 'Animal',
12718     excerpt: 'No way dude!'
12719 });
12720 myStore.add(myNewRecord);
12721 </code></pre>
12722  * @method create
12723  * @static
12724  */
12725 Roo.data.Record.create = function(o){
12726     var f = function(){
12727         f.superclass.constructor.apply(this, arguments);
12728     };
12729     Roo.extend(f, Roo.data.Record);
12730     var p = f.prototype;
12731     p.fields = new Roo.util.MixedCollection(false, function(field){
12732         return field.name;
12733     });
12734     for(var i = 0, len = o.length; i < len; i++){
12735         p.fields.add(new Roo.data.Field(o[i]));
12736     }
12737     f.getField = function(name){
12738         return p.fields.get(name);  
12739     };
12740     return f;
12741 };
12742
12743 Roo.data.Record.AUTO_ID = 1000;
12744 Roo.data.Record.EDIT = 'edit';
12745 Roo.data.Record.REJECT = 'reject';
12746 Roo.data.Record.COMMIT = 'commit';
12747
12748 Roo.data.Record.prototype = {
12749     /**
12750      * Readonly flag - true if this record has been modified.
12751      * @type Boolean
12752      */
12753     dirty : false,
12754     editing : false,
12755     error: null,
12756     modified: null,
12757
12758     // private
12759     join : function(store){
12760         this.store = store;
12761     },
12762
12763     /**
12764      * Set the named field to the specified value.
12765      * @param {String} name The name of the field to set.
12766      * @param {Object} value The value to set the field to.
12767      */
12768     set : function(name, value){
12769         if(this.data[name] == value){
12770             return;
12771         }
12772         this.dirty = true;
12773         if(!this.modified){
12774             this.modified = {};
12775         }
12776         if(typeof this.modified[name] == 'undefined'){
12777             this.modified[name] = this.data[name];
12778         }
12779         this.data[name] = value;
12780         if(!this.editing && this.store){
12781             this.store.afterEdit(this);
12782         }       
12783     },
12784
12785     /**
12786      * Get the value of the named field.
12787      * @param {String} name The name of the field to get the value of.
12788      * @return {Object} The value of the field.
12789      */
12790     get : function(name){
12791         return this.data[name]; 
12792     },
12793
12794     // private
12795     beginEdit : function(){
12796         this.editing = true;
12797         this.modified = {}; 
12798     },
12799
12800     // private
12801     cancelEdit : function(){
12802         this.editing = false;
12803         delete this.modified;
12804     },
12805
12806     // private
12807     endEdit : function(){
12808         this.editing = false;
12809         if(this.dirty && this.store){
12810             this.store.afterEdit(this);
12811         }
12812     },
12813
12814     /**
12815      * Usually called by the {@link Roo.data.Store} which owns the Record.
12816      * Rejects all changes made to the Record since either creation, or the last commit operation.
12817      * Modified fields are reverted to their original values.
12818      * <p>
12819      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12820      * of reject operations.
12821      */
12822     reject : function(){
12823         var m = this.modified;
12824         for(var n in m){
12825             if(typeof m[n] != "function"){
12826                 this.data[n] = m[n];
12827             }
12828         }
12829         this.dirty = false;
12830         delete this.modified;
12831         this.editing = false;
12832         if(this.store){
12833             this.store.afterReject(this);
12834         }
12835     },
12836
12837     /**
12838      * Usually called by the {@link Roo.data.Store} which owns the Record.
12839      * Commits all changes made to the Record since either creation, or the last commit operation.
12840      * <p>
12841      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12842      * of commit operations.
12843      */
12844     commit : function(){
12845         this.dirty = false;
12846         delete this.modified;
12847         this.editing = false;
12848         if(this.store){
12849             this.store.afterCommit(this);
12850         }
12851     },
12852
12853     // private
12854     hasError : function(){
12855         return this.error != null;
12856     },
12857
12858     // private
12859     clearError : function(){
12860         this.error = null;
12861     },
12862
12863     /**
12864      * Creates a copy of this record.
12865      * @param {String} id (optional) A new record id if you don't want to use this record's id
12866      * @return {Record}
12867      */
12868     copy : function(newId) {
12869         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12870     }
12871 };/*
12872  * Based on:
12873  * Ext JS Library 1.1.1
12874  * Copyright(c) 2006-2007, Ext JS, LLC.
12875  *
12876  * Originally Released Under LGPL - original licence link has changed is not relivant.
12877  *
12878  * Fork - LGPL
12879  * <script type="text/javascript">
12880  */
12881
12882
12883
12884 /**
12885  * @class Roo.data.Store
12886  * @extends Roo.util.Observable
12887  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12888  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12889  * <p>
12890  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
12891  * has no knowledge of the format of the data returned by the Proxy.<br>
12892  * <p>
12893  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
12894  * instances from the data object. These records are cached and made available through accessor functions.
12895  * @constructor
12896  * Creates a new Store.
12897  * @param {Object} config A config object containing the objects needed for the Store to access data,
12898  * and read the data into Records.
12899  */
12900 Roo.data.Store = function(config){
12901     this.data = new Roo.util.MixedCollection(false);
12902     this.data.getKey = function(o){
12903         return o.id;
12904     };
12905     this.baseParams = {};
12906     // private
12907     this.paramNames = {
12908         "start" : "start",
12909         "limit" : "limit",
12910         "sort" : "sort",
12911         "dir" : "dir",
12912         "multisort" : "_multisort"
12913     };
12914
12915     if(config && config.data){
12916         this.inlineData = config.data;
12917         delete config.data;
12918     }
12919
12920     Roo.apply(this, config);
12921     
12922     if(this.reader){ // reader passed
12923         this.reader = Roo.factory(this.reader, Roo.data);
12924         this.reader.xmodule = this.xmodule || false;
12925         if(!this.recordType){
12926             this.recordType = this.reader.recordType;
12927         }
12928         if(this.reader.onMetaChange){
12929             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
12930         }
12931     }
12932
12933     if(this.recordType){
12934         this.fields = this.recordType.prototype.fields;
12935     }
12936     this.modified = [];
12937
12938     this.addEvents({
12939         /**
12940          * @event datachanged
12941          * Fires when the data cache has changed, and a widget which is using this Store
12942          * as a Record cache should refresh its view.
12943          * @param {Store} this
12944          */
12945         datachanged : true,
12946         /**
12947          * @event metachange
12948          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
12949          * @param {Store} this
12950          * @param {Object} meta The JSON metadata
12951          */
12952         metachange : true,
12953         /**
12954          * @event add
12955          * Fires when Records have been added to the Store
12956          * @param {Store} this
12957          * @param {Roo.data.Record[]} records The array of Records added
12958          * @param {Number} index The index at which the record(s) were added
12959          */
12960         add : true,
12961         /**
12962          * @event remove
12963          * Fires when a Record has been removed from the Store
12964          * @param {Store} this
12965          * @param {Roo.data.Record} record The Record that was removed
12966          * @param {Number} index The index at which the record was removed
12967          */
12968         remove : true,
12969         /**
12970          * @event update
12971          * Fires when a Record has been updated
12972          * @param {Store} this
12973          * @param {Roo.data.Record} record The Record that was updated
12974          * @param {String} operation The update operation being performed.  Value may be one of:
12975          * <pre><code>
12976  Roo.data.Record.EDIT
12977  Roo.data.Record.REJECT
12978  Roo.data.Record.COMMIT
12979          * </code></pre>
12980          */
12981         update : true,
12982         /**
12983          * @event clear
12984          * Fires when the data cache has been cleared.
12985          * @param {Store} this
12986          */
12987         clear : true,
12988         /**
12989          * @event beforeload
12990          * Fires before a request is made for a new data object.  If the beforeload handler returns false
12991          * the load action will be canceled.
12992          * @param {Store} this
12993          * @param {Object} options The loading options that were specified (see {@link #load} for details)
12994          */
12995         beforeload : true,
12996         /**
12997          * @event beforeloadadd
12998          * Fires after a new set of Records has been loaded.
12999          * @param {Store} this
13000          * @param {Roo.data.Record[]} records The Records that were loaded
13001          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13002          */
13003         beforeloadadd : true,
13004         /**
13005          * @event load
13006          * Fires after a new set of Records has been loaded, before they are added to the store.
13007          * @param {Store} this
13008          * @param {Roo.data.Record[]} records The Records that were loaded
13009          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13010          * @params {Object} return from reader
13011          */
13012         load : true,
13013         /**
13014          * @event loadexception
13015          * Fires if an exception occurs in the Proxy during loading.
13016          * Called with the signature of the Proxy's "loadexception" event.
13017          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13018          * 
13019          * @param {Proxy} 
13020          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13021          * @param {Object} load options 
13022          * @param {Object} jsonData from your request (normally this contains the Exception)
13023          */
13024         loadexception : true
13025     });
13026     
13027     if(this.proxy){
13028         this.proxy = Roo.factory(this.proxy, Roo.data);
13029         this.proxy.xmodule = this.xmodule || false;
13030         this.relayEvents(this.proxy,  ["loadexception"]);
13031     }
13032     this.sortToggle = {};
13033     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13034
13035     Roo.data.Store.superclass.constructor.call(this);
13036
13037     if(this.inlineData){
13038         this.loadData(this.inlineData);
13039         delete this.inlineData;
13040     }
13041 };
13042
13043 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13044      /**
13045     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13046     * without a remote query - used by combo/forms at present.
13047     */
13048     
13049     /**
13050     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13051     */
13052     /**
13053     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13054     */
13055     /**
13056     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13057     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13058     */
13059     /**
13060     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13061     * on any HTTP request
13062     */
13063     /**
13064     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13065     */
13066     /**
13067     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13068     */
13069     multiSort: false,
13070     /**
13071     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13072     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13073     */
13074     remoteSort : false,
13075
13076     /**
13077     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13078      * loaded or when a record is removed. (defaults to false).
13079     */
13080     pruneModifiedRecords : false,
13081
13082     // private
13083     lastOptions : null,
13084
13085     /**
13086      * Add Records to the Store and fires the add event.
13087      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13088      */
13089     add : function(records){
13090         records = [].concat(records);
13091         for(var i = 0, len = records.length; i < len; i++){
13092             records[i].join(this);
13093         }
13094         var index = this.data.length;
13095         this.data.addAll(records);
13096         this.fireEvent("add", this, records, index);
13097     },
13098
13099     /**
13100      * Remove a Record from the Store and fires the remove event.
13101      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13102      */
13103     remove : function(record){
13104         var index = this.data.indexOf(record);
13105         this.data.removeAt(index);
13106  
13107         if(this.pruneModifiedRecords){
13108             this.modified.remove(record);
13109         }
13110         this.fireEvent("remove", this, record, index);
13111     },
13112
13113     /**
13114      * Remove all Records from the Store and fires the clear event.
13115      */
13116     removeAll : function(){
13117         this.data.clear();
13118         if(this.pruneModifiedRecords){
13119             this.modified = [];
13120         }
13121         this.fireEvent("clear", this);
13122     },
13123
13124     /**
13125      * Inserts Records to the Store at the given index and fires the add event.
13126      * @param {Number} index The start index at which to insert the passed Records.
13127      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13128      */
13129     insert : function(index, records){
13130         records = [].concat(records);
13131         for(var i = 0, len = records.length; i < len; i++){
13132             this.data.insert(index, records[i]);
13133             records[i].join(this);
13134         }
13135         this.fireEvent("add", this, records, index);
13136     },
13137
13138     /**
13139      * Get the index within the cache of the passed Record.
13140      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13141      * @return {Number} The index of the passed Record. Returns -1 if not found.
13142      */
13143     indexOf : function(record){
13144         return this.data.indexOf(record);
13145     },
13146
13147     /**
13148      * Get the index within the cache of the Record with the passed id.
13149      * @param {String} id The id of the Record to find.
13150      * @return {Number} The index of the Record. Returns -1 if not found.
13151      */
13152     indexOfId : function(id){
13153         return this.data.indexOfKey(id);
13154     },
13155
13156     /**
13157      * Get the Record with the specified id.
13158      * @param {String} id The id of the Record to find.
13159      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13160      */
13161     getById : function(id){
13162         return this.data.key(id);
13163     },
13164
13165     /**
13166      * Get the Record at the specified index.
13167      * @param {Number} index The index of the Record to find.
13168      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13169      */
13170     getAt : function(index){
13171         return this.data.itemAt(index);
13172     },
13173
13174     /**
13175      * Returns a range of Records between specified indices.
13176      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13177      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13178      * @return {Roo.data.Record[]} An array of Records
13179      */
13180     getRange : function(start, end){
13181         return this.data.getRange(start, end);
13182     },
13183
13184     // private
13185     storeOptions : function(o){
13186         o = Roo.apply({}, o);
13187         delete o.callback;
13188         delete o.scope;
13189         this.lastOptions = o;
13190     },
13191
13192     /**
13193      * Loads the Record cache from the configured Proxy using the configured Reader.
13194      * <p>
13195      * If using remote paging, then the first load call must specify the <em>start</em>
13196      * and <em>limit</em> properties in the options.params property to establish the initial
13197      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13198      * <p>
13199      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13200      * and this call will return before the new data has been loaded. Perform any post-processing
13201      * in a callback function, or in a "load" event handler.</strong>
13202      * <p>
13203      * @param {Object} options An object containing properties which control loading options:<ul>
13204      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13205      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13206      * passed the following arguments:<ul>
13207      * <li>r : Roo.data.Record[]</li>
13208      * <li>options: Options object from the load call</li>
13209      * <li>success: Boolean success indicator</li></ul></li>
13210      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13211      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13212      * </ul>
13213      */
13214     load : function(options){
13215         options = options || {};
13216         if(this.fireEvent("beforeload", this, options) !== false){
13217             this.storeOptions(options);
13218             var p = Roo.apply(options.params || {}, this.baseParams);
13219             // if meta was not loaded from remote source.. try requesting it.
13220             if (!this.reader.metaFromRemote) {
13221                 p._requestMeta = 1;
13222             }
13223             if(this.sortInfo && this.remoteSort){
13224                 var pn = this.paramNames;
13225                 p[pn["sort"]] = this.sortInfo.field;
13226                 p[pn["dir"]] = this.sortInfo.direction;
13227             }
13228             if (this.multiSort) {
13229                 var pn = this.paramNames;
13230                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13231             }
13232             
13233             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13234         }
13235     },
13236
13237     /**
13238      * Reloads the Record cache from the configured Proxy using the configured Reader and
13239      * the options from the last load operation performed.
13240      * @param {Object} options (optional) An object containing properties which may override the options
13241      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13242      * the most recently used options are reused).
13243      */
13244     reload : function(options){
13245         this.load(Roo.applyIf(options||{}, this.lastOptions));
13246     },
13247
13248     // private
13249     // Called as a callback by the Reader during a load operation.
13250     loadRecords : function(o, options, success){
13251         if(!o || success === false){
13252             if(success !== false){
13253                 this.fireEvent("load", this, [], options, o);
13254             }
13255             if(options.callback){
13256                 options.callback.call(options.scope || this, [], options, false);
13257             }
13258             return;
13259         }
13260         // if data returned failure - throw an exception.
13261         if (o.success === false) {
13262             // show a message if no listener is registered.
13263             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13264                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13265             }
13266             // loadmask wil be hooked into this..
13267             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13268             return;
13269         }
13270         var r = o.records, t = o.totalRecords || r.length;
13271         
13272         this.fireEvent("beforeloadadd", this, r, options, o);
13273         
13274         if(!options || options.add !== true){
13275             if(this.pruneModifiedRecords){
13276                 this.modified = [];
13277             }
13278             for(var i = 0, len = r.length; i < len; i++){
13279                 r[i].join(this);
13280             }
13281             if(this.snapshot){
13282                 this.data = this.snapshot;
13283                 delete this.snapshot;
13284             }
13285             this.data.clear();
13286             this.data.addAll(r);
13287             this.totalLength = t;
13288             this.applySort();
13289             this.fireEvent("datachanged", this);
13290         }else{
13291             this.totalLength = Math.max(t, this.data.length+r.length);
13292             this.add(r);
13293         }
13294         
13295         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13296                 
13297             var e = new Roo.data.Record({});
13298
13299             e.set(this.parent.displayField, this.parent.emptyTitle);
13300             e.set(this.parent.valueField, '');
13301
13302             this.insert(0, e);
13303         }
13304             
13305         this.fireEvent("load", this, r, options, o);
13306         if(options.callback){
13307             options.callback.call(options.scope || this, r, options, true);
13308         }
13309     },
13310
13311
13312     /**
13313      * Loads data from a passed data block. A Reader which understands the format of the data
13314      * must have been configured in the constructor.
13315      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13316      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13317      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13318      */
13319     loadData : function(o, append){
13320         var r = this.reader.readRecords(o);
13321         this.loadRecords(r, {add: append}, true);
13322     },
13323     
13324      /**
13325      * using 'cn' the nested child reader read the child array into it's child stores.
13326      * @param {Object} rec The record with a 'children array
13327      */
13328     loadDataFromChildren : function(rec)
13329     {
13330         this.loadData(this.reader.toLoadData(rec));
13331     },
13332     
13333
13334     /**
13335      * Gets the number of cached records.
13336      * <p>
13337      * <em>If using paging, this may not be the total size of the dataset. If the data object
13338      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13339      * the data set size</em>
13340      */
13341     getCount : function(){
13342         return this.data.length || 0;
13343     },
13344
13345     /**
13346      * Gets the total number of records in the dataset as returned by the server.
13347      * <p>
13348      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13349      * the dataset size</em>
13350      */
13351     getTotalCount : function(){
13352         return this.totalLength || 0;
13353     },
13354
13355     /**
13356      * Returns the sort state of the Store as an object with two properties:
13357      * <pre><code>
13358  field {String} The name of the field by which the Records are sorted
13359  direction {String} The sort order, "ASC" or "DESC"
13360      * </code></pre>
13361      */
13362     getSortState : function(){
13363         return this.sortInfo;
13364     },
13365
13366     // private
13367     applySort : function(){
13368         if(this.sortInfo && !this.remoteSort){
13369             var s = this.sortInfo, f = s.field;
13370             var st = this.fields.get(f).sortType;
13371             var fn = function(r1, r2){
13372                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13373                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13374             };
13375             this.data.sort(s.direction, fn);
13376             if(this.snapshot && this.snapshot != this.data){
13377                 this.snapshot.sort(s.direction, fn);
13378             }
13379         }
13380     },
13381
13382     /**
13383      * Sets the default sort column and order to be used by the next load operation.
13384      * @param {String} fieldName The name of the field to sort by.
13385      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13386      */
13387     setDefaultSort : function(field, dir){
13388         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13389     },
13390
13391     /**
13392      * Sort the Records.
13393      * If remote sorting is used, the sort is performed on the server, and the cache is
13394      * reloaded. If local sorting is used, the cache is sorted internally.
13395      * @param {String} fieldName The name of the field to sort by.
13396      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13397      */
13398     sort : function(fieldName, dir){
13399         var f = this.fields.get(fieldName);
13400         if(!dir){
13401             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13402             
13403             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13404                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13405             }else{
13406                 dir = f.sortDir;
13407             }
13408         }
13409         this.sortToggle[f.name] = dir;
13410         this.sortInfo = {field: f.name, direction: dir};
13411         if(!this.remoteSort){
13412             this.applySort();
13413             this.fireEvent("datachanged", this);
13414         }else{
13415             this.load(this.lastOptions);
13416         }
13417     },
13418
13419     /**
13420      * Calls the specified function for each of the Records in the cache.
13421      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13422      * Returning <em>false</em> aborts and exits the iteration.
13423      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13424      */
13425     each : function(fn, scope){
13426         this.data.each(fn, scope);
13427     },
13428
13429     /**
13430      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13431      * (e.g., during paging).
13432      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13433      */
13434     getModifiedRecords : function(){
13435         return this.modified;
13436     },
13437
13438     // private
13439     createFilterFn : function(property, value, anyMatch){
13440         if(!value.exec){ // not a regex
13441             value = String(value);
13442             if(value.length == 0){
13443                 return false;
13444             }
13445             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13446         }
13447         return function(r){
13448             return value.test(r.data[property]);
13449         };
13450     },
13451
13452     /**
13453      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13454      * @param {String} property A field on your records
13455      * @param {Number} start The record index to start at (defaults to 0)
13456      * @param {Number} end The last record index to include (defaults to length - 1)
13457      * @return {Number} The sum
13458      */
13459     sum : function(property, start, end){
13460         var rs = this.data.items, v = 0;
13461         start = start || 0;
13462         end = (end || end === 0) ? end : rs.length-1;
13463
13464         for(var i = start; i <= end; i++){
13465             v += (rs[i].data[property] || 0);
13466         }
13467         return v;
13468     },
13469
13470     /**
13471      * Filter the records by a specified property.
13472      * @param {String} field A field on your records
13473      * @param {String/RegExp} value Either a string that the field
13474      * should start with or a RegExp to test against the field
13475      * @param {Boolean} anyMatch True to match any part not just the beginning
13476      */
13477     filter : function(property, value, anyMatch){
13478         var fn = this.createFilterFn(property, value, anyMatch);
13479         return fn ? this.filterBy(fn) : this.clearFilter();
13480     },
13481
13482     /**
13483      * Filter by a function. The specified function will be called with each
13484      * record in this data source. If the function returns true the record is included,
13485      * otherwise it is filtered.
13486      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13487      * @param {Object} scope (optional) The scope of the function (defaults to this)
13488      */
13489     filterBy : function(fn, scope){
13490         this.snapshot = this.snapshot || this.data;
13491         this.data = this.queryBy(fn, scope||this);
13492         this.fireEvent("datachanged", this);
13493     },
13494
13495     /**
13496      * Query the records by a specified property.
13497      * @param {String} field A field on your records
13498      * @param {String/RegExp} value Either a string that the field
13499      * should start with or a RegExp to test against the field
13500      * @param {Boolean} anyMatch True to match any part not just the beginning
13501      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13502      */
13503     query : function(property, value, anyMatch){
13504         var fn = this.createFilterFn(property, value, anyMatch);
13505         return fn ? this.queryBy(fn) : this.data.clone();
13506     },
13507
13508     /**
13509      * Query by a function. The specified function will be called with each
13510      * record in this data source. If the function returns true the record is included
13511      * in the results.
13512      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13513      * @param {Object} scope (optional) The scope of the function (defaults to this)
13514       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13515      **/
13516     queryBy : function(fn, scope){
13517         var data = this.snapshot || this.data;
13518         return data.filterBy(fn, scope||this);
13519     },
13520
13521     /**
13522      * Collects unique values for a particular dataIndex from this store.
13523      * @param {String} dataIndex The property to collect
13524      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13525      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13526      * @return {Array} An array of the unique values
13527      **/
13528     collect : function(dataIndex, allowNull, bypassFilter){
13529         var d = (bypassFilter === true && this.snapshot) ?
13530                 this.snapshot.items : this.data.items;
13531         var v, sv, r = [], l = {};
13532         for(var i = 0, len = d.length; i < len; i++){
13533             v = d[i].data[dataIndex];
13534             sv = String(v);
13535             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13536                 l[sv] = true;
13537                 r[r.length] = v;
13538             }
13539         }
13540         return r;
13541     },
13542
13543     /**
13544      * Revert to a view of the Record cache with no filtering applied.
13545      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13546      */
13547     clearFilter : function(suppressEvent){
13548         if(this.snapshot && this.snapshot != this.data){
13549             this.data = this.snapshot;
13550             delete this.snapshot;
13551             if(suppressEvent !== true){
13552                 this.fireEvent("datachanged", this);
13553             }
13554         }
13555     },
13556
13557     // private
13558     afterEdit : function(record){
13559         if(this.modified.indexOf(record) == -1){
13560             this.modified.push(record);
13561         }
13562         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13563     },
13564     
13565     // private
13566     afterReject : function(record){
13567         this.modified.remove(record);
13568         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13569     },
13570
13571     // private
13572     afterCommit : function(record){
13573         this.modified.remove(record);
13574         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13575     },
13576
13577     /**
13578      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13579      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13580      */
13581     commitChanges : function(){
13582         var m = this.modified.slice(0);
13583         this.modified = [];
13584         for(var i = 0, len = m.length; i < len; i++){
13585             m[i].commit();
13586         }
13587     },
13588
13589     /**
13590      * Cancel outstanding changes on all changed records.
13591      */
13592     rejectChanges : function(){
13593         var m = this.modified.slice(0);
13594         this.modified = [];
13595         for(var i = 0, len = m.length; i < len; i++){
13596             m[i].reject();
13597         }
13598     },
13599
13600     onMetaChange : function(meta, rtype, o){
13601         this.recordType = rtype;
13602         this.fields = rtype.prototype.fields;
13603         delete this.snapshot;
13604         this.sortInfo = meta.sortInfo || this.sortInfo;
13605         this.modified = [];
13606         this.fireEvent('metachange', this, this.reader.meta);
13607     },
13608     
13609     moveIndex : function(data, type)
13610     {
13611         var index = this.indexOf(data);
13612         
13613         var newIndex = index + type;
13614         
13615         this.remove(data);
13616         
13617         this.insert(newIndex, data);
13618         
13619     }
13620 });/*
13621  * Based on:
13622  * Ext JS Library 1.1.1
13623  * Copyright(c) 2006-2007, Ext JS, LLC.
13624  *
13625  * Originally Released Under LGPL - original licence link has changed is not relivant.
13626  *
13627  * Fork - LGPL
13628  * <script type="text/javascript">
13629  */
13630
13631 /**
13632  * @class Roo.data.SimpleStore
13633  * @extends Roo.data.Store
13634  * Small helper class to make creating Stores from Array data easier.
13635  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13636  * @cfg {Array} fields An array of field definition objects, or field name strings.
13637  * @cfg {Object} an existing reader (eg. copied from another store)
13638  * @cfg {Array} data The multi-dimensional array of data
13639  * @constructor
13640  * @param {Object} config
13641  */
13642 Roo.data.SimpleStore = function(config)
13643 {
13644     Roo.data.SimpleStore.superclass.constructor.call(this, {
13645         isLocal : true,
13646         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13647                 id: config.id
13648             },
13649             Roo.data.Record.create(config.fields)
13650         ),
13651         proxy : new Roo.data.MemoryProxy(config.data)
13652     });
13653     this.load();
13654 };
13655 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13656  * Based on:
13657  * Ext JS Library 1.1.1
13658  * Copyright(c) 2006-2007, Ext JS, LLC.
13659  *
13660  * Originally Released Under LGPL - original licence link has changed is not relivant.
13661  *
13662  * Fork - LGPL
13663  * <script type="text/javascript">
13664  */
13665
13666 /**
13667 /**
13668  * @extends Roo.data.Store
13669  * @class Roo.data.JsonStore
13670  * Small helper class to make creating Stores for JSON data easier. <br/>
13671 <pre><code>
13672 var store = new Roo.data.JsonStore({
13673     url: 'get-images.php',
13674     root: 'images',
13675     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13676 });
13677 </code></pre>
13678  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13679  * JsonReader and HttpProxy (unless inline data is provided).</b>
13680  * @cfg {Array} fields An array of field definition objects, or field name strings.
13681  * @constructor
13682  * @param {Object} config
13683  */
13684 Roo.data.JsonStore = function(c){
13685     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13686         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13687         reader: new Roo.data.JsonReader(c, c.fields)
13688     }));
13689 };
13690 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13691  * Based on:
13692  * Ext JS Library 1.1.1
13693  * Copyright(c) 2006-2007, Ext JS, LLC.
13694  *
13695  * Originally Released Under LGPL - original licence link has changed is not relivant.
13696  *
13697  * Fork - LGPL
13698  * <script type="text/javascript">
13699  */
13700
13701  
13702 Roo.data.Field = function(config){
13703     if(typeof config == "string"){
13704         config = {name: config};
13705     }
13706     Roo.apply(this, config);
13707     
13708     if(!this.type){
13709         this.type = "auto";
13710     }
13711     
13712     var st = Roo.data.SortTypes;
13713     // named sortTypes are supported, here we look them up
13714     if(typeof this.sortType == "string"){
13715         this.sortType = st[this.sortType];
13716     }
13717     
13718     // set default sortType for strings and dates
13719     if(!this.sortType){
13720         switch(this.type){
13721             case "string":
13722                 this.sortType = st.asUCString;
13723                 break;
13724             case "date":
13725                 this.sortType = st.asDate;
13726                 break;
13727             default:
13728                 this.sortType = st.none;
13729         }
13730     }
13731
13732     // define once
13733     var stripRe = /[\$,%]/g;
13734
13735     // prebuilt conversion function for this field, instead of
13736     // switching every time we're reading a value
13737     if(!this.convert){
13738         var cv, dateFormat = this.dateFormat;
13739         switch(this.type){
13740             case "":
13741             case "auto":
13742             case undefined:
13743                 cv = function(v){ return v; };
13744                 break;
13745             case "string":
13746                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13747                 break;
13748             case "int":
13749                 cv = function(v){
13750                     return v !== undefined && v !== null && v !== '' ?
13751                            parseInt(String(v).replace(stripRe, ""), 10) : '';
13752                     };
13753                 break;
13754             case "float":
13755                 cv = function(v){
13756                     return v !== undefined && v !== null && v !== '' ?
13757                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
13758                     };
13759                 break;
13760             case "bool":
13761             case "boolean":
13762                 cv = function(v){ return v === true || v === "true" || v == 1; };
13763                 break;
13764             case "date":
13765                 cv = function(v){
13766                     if(!v){
13767                         return '';
13768                     }
13769                     if(v instanceof Date){
13770                         return v;
13771                     }
13772                     if(dateFormat){
13773                         if(dateFormat == "timestamp"){
13774                             return new Date(v*1000);
13775                         }
13776                         return Date.parseDate(v, dateFormat);
13777                     }
13778                     var parsed = Date.parse(v);
13779                     return parsed ? new Date(parsed) : null;
13780                 };
13781              break;
13782             
13783         }
13784         this.convert = cv;
13785     }
13786 };
13787
13788 Roo.data.Field.prototype = {
13789     dateFormat: null,
13790     defaultValue: "",
13791     mapping: null,
13792     sortType : null,
13793     sortDir : "ASC"
13794 };/*
13795  * Based on:
13796  * Ext JS Library 1.1.1
13797  * Copyright(c) 2006-2007, Ext JS, LLC.
13798  *
13799  * Originally Released Under LGPL - original licence link has changed is not relivant.
13800  *
13801  * Fork - LGPL
13802  * <script type="text/javascript">
13803  */
13804  
13805 // Base class for reading structured data from a data source.  This class is intended to be
13806 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13807
13808 /**
13809  * @class Roo.data.DataReader
13810  * Base class for reading structured data from a data source.  This class is intended to be
13811  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13812  */
13813
13814 Roo.data.DataReader = function(meta, recordType){
13815     
13816     this.meta = meta;
13817     
13818     this.recordType = recordType instanceof Array ? 
13819         Roo.data.Record.create(recordType) : recordType;
13820 };
13821
13822 Roo.data.DataReader.prototype = {
13823     
13824     
13825     readerType : 'Data',
13826      /**
13827      * Create an empty record
13828      * @param {Object} data (optional) - overlay some values
13829      * @return {Roo.data.Record} record created.
13830      */
13831     newRow :  function(d) {
13832         var da =  {};
13833         this.recordType.prototype.fields.each(function(c) {
13834             switch( c.type) {
13835                 case 'int' : da[c.name] = 0; break;
13836                 case 'date' : da[c.name] = new Date(); break;
13837                 case 'float' : da[c.name] = 0.0; break;
13838                 case 'boolean' : da[c.name] = false; break;
13839                 default : da[c.name] = ""; break;
13840             }
13841             
13842         });
13843         return new this.recordType(Roo.apply(da, d));
13844     }
13845     
13846     
13847 };/*
13848  * Based on:
13849  * Ext JS Library 1.1.1
13850  * Copyright(c) 2006-2007, Ext JS, LLC.
13851  *
13852  * Originally Released Under LGPL - original licence link has changed is not relivant.
13853  *
13854  * Fork - LGPL
13855  * <script type="text/javascript">
13856  */
13857
13858 /**
13859  * @class Roo.data.DataProxy
13860  * @extends Roo.data.Observable
13861  * This class is an abstract base class for implementations which provide retrieval of
13862  * unformatted data objects.<br>
13863  * <p>
13864  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13865  * (of the appropriate type which knows how to parse the data object) to provide a block of
13866  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13867  * <p>
13868  * Custom implementations must implement the load method as described in
13869  * {@link Roo.data.HttpProxy#load}.
13870  */
13871 Roo.data.DataProxy = function(){
13872     this.addEvents({
13873         /**
13874          * @event beforeload
13875          * Fires before a network request is made to retrieve a data object.
13876          * @param {Object} This DataProxy object.
13877          * @param {Object} params The params parameter to the load function.
13878          */
13879         beforeload : true,
13880         /**
13881          * @event load
13882          * Fires before the load method's callback is called.
13883          * @param {Object} This DataProxy object.
13884          * @param {Object} o The data object.
13885          * @param {Object} arg The callback argument object passed to the load function.
13886          */
13887         load : true,
13888         /**
13889          * @event loadexception
13890          * Fires if an Exception occurs during data retrieval.
13891          * @param {Object} This DataProxy object.
13892          * @param {Object} o The data object.
13893          * @param {Object} arg The callback argument object passed to the load function.
13894          * @param {Object} e The Exception.
13895          */
13896         loadexception : true
13897     });
13898     Roo.data.DataProxy.superclass.constructor.call(this);
13899 };
13900
13901 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
13902
13903     /**
13904      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
13905      */
13906 /*
13907  * Based on:
13908  * Ext JS Library 1.1.1
13909  * Copyright(c) 2006-2007, Ext JS, LLC.
13910  *
13911  * Originally Released Under LGPL - original licence link has changed is not relivant.
13912  *
13913  * Fork - LGPL
13914  * <script type="text/javascript">
13915  */
13916 /**
13917  * @class Roo.data.MemoryProxy
13918  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
13919  * to the Reader when its load method is called.
13920  * @constructor
13921  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
13922  */
13923 Roo.data.MemoryProxy = function(data){
13924     if (data.data) {
13925         data = data.data;
13926     }
13927     Roo.data.MemoryProxy.superclass.constructor.call(this);
13928     this.data = data;
13929 };
13930
13931 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
13932     
13933     /**
13934      * Load data from the requested source (in this case an in-memory
13935      * data object passed to the constructor), read the data object into
13936      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13937      * process that block using the passed callback.
13938      * @param {Object} params This parameter is not used by the MemoryProxy class.
13939      * @param {Roo.data.DataReader} reader The Reader object which converts the data
13940      * object into a block of Roo.data.Records.
13941      * @param {Function} callback The function into which to pass the block of Roo.data.records.
13942      * The function must be passed <ul>
13943      * <li>The Record block object</li>
13944      * <li>The "arg" argument from the load function</li>
13945      * <li>A boolean success indicator</li>
13946      * </ul>
13947      * @param {Object} scope The scope in which to call the callback
13948      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13949      */
13950     load : function(params, reader, callback, scope, arg){
13951         params = params || {};
13952         var result;
13953         try {
13954             result = reader.readRecords(params.data ? params.data :this.data);
13955         }catch(e){
13956             this.fireEvent("loadexception", this, arg, null, e);
13957             callback.call(scope, null, arg, false);
13958             return;
13959         }
13960         callback.call(scope, result, arg, true);
13961     },
13962     
13963     // private
13964     update : function(params, records){
13965         
13966     }
13967 });/*
13968  * Based on:
13969  * Ext JS Library 1.1.1
13970  * Copyright(c) 2006-2007, Ext JS, LLC.
13971  *
13972  * Originally Released Under LGPL - original licence link has changed is not relivant.
13973  *
13974  * Fork - LGPL
13975  * <script type="text/javascript">
13976  */
13977 /**
13978  * @class Roo.data.HttpProxy
13979  * @extends Roo.data.DataProxy
13980  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
13981  * configured to reference a certain URL.<br><br>
13982  * <p>
13983  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
13984  * from which the running page was served.<br><br>
13985  * <p>
13986  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
13987  * <p>
13988  * Be aware that to enable the browser to parse an XML document, the server must set
13989  * the Content-Type header in the HTTP response to "text/xml".
13990  * @constructor
13991  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
13992  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
13993  * will be used to make the request.
13994  */
13995 Roo.data.HttpProxy = function(conn){
13996     Roo.data.HttpProxy.superclass.constructor.call(this);
13997     // is conn a conn config or a real conn?
13998     this.conn = conn;
13999     this.useAjax = !conn || !conn.events;
14000   
14001 };
14002
14003 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14004     // thse are take from connection...
14005     
14006     /**
14007      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14008      */
14009     /**
14010      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14011      * extra parameters to each request made by this object. (defaults to undefined)
14012      */
14013     /**
14014      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14015      *  to each request made by this object. (defaults to undefined)
14016      */
14017     /**
14018      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
14019      */
14020     /**
14021      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14022      */
14023      /**
14024      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14025      * @type Boolean
14026      */
14027   
14028
14029     /**
14030      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14031      * @type Boolean
14032      */
14033     /**
14034      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14035      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14036      * a finer-grained basis than the DataProxy events.
14037      */
14038     getConnection : function(){
14039         return this.useAjax ? Roo.Ajax : this.conn;
14040     },
14041
14042     /**
14043      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14044      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14045      * process that block using the passed callback.
14046      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14047      * for the request to the remote server.
14048      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14049      * object into a block of Roo.data.Records.
14050      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14051      * The function must be passed <ul>
14052      * <li>The Record block object</li>
14053      * <li>The "arg" argument from the load function</li>
14054      * <li>A boolean success indicator</li>
14055      * </ul>
14056      * @param {Object} scope The scope in which to call the callback
14057      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14058      */
14059     load : function(params, reader, callback, scope, arg){
14060         if(this.fireEvent("beforeload", this, params) !== false){
14061             var  o = {
14062                 params : params || {},
14063                 request: {
14064                     callback : callback,
14065                     scope : scope,
14066                     arg : arg
14067                 },
14068                 reader: reader,
14069                 callback : this.loadResponse,
14070                 scope: this
14071             };
14072             if(this.useAjax){
14073                 Roo.applyIf(o, this.conn);
14074                 if(this.activeRequest){
14075                     Roo.Ajax.abort(this.activeRequest);
14076                 }
14077                 this.activeRequest = Roo.Ajax.request(o);
14078             }else{
14079                 this.conn.request(o);
14080             }
14081         }else{
14082             callback.call(scope||this, null, arg, false);
14083         }
14084     },
14085
14086     // private
14087     loadResponse : function(o, success, response){
14088         delete this.activeRequest;
14089         if(!success){
14090             this.fireEvent("loadexception", this, o, response);
14091             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14092             return;
14093         }
14094         var result;
14095         try {
14096             result = o.reader.read(response);
14097         }catch(e){
14098             this.fireEvent("loadexception", this, o, response, e);
14099             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14100             return;
14101         }
14102         
14103         this.fireEvent("load", this, o, o.request.arg);
14104         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14105     },
14106
14107     // private
14108     update : function(dataSet){
14109
14110     },
14111
14112     // private
14113     updateResponse : function(dataSet){
14114
14115     }
14116 });/*
14117  * Based on:
14118  * Ext JS Library 1.1.1
14119  * Copyright(c) 2006-2007, Ext JS, LLC.
14120  *
14121  * Originally Released Under LGPL - original licence link has changed is not relivant.
14122  *
14123  * Fork - LGPL
14124  * <script type="text/javascript">
14125  */
14126
14127 /**
14128  * @class Roo.data.ScriptTagProxy
14129  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14130  * other than the originating domain of the running page.<br><br>
14131  * <p>
14132  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
14133  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14134  * <p>
14135  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14136  * source code that is used as the source inside a &lt;script> tag.<br><br>
14137  * <p>
14138  * In order for the browser to process the returned data, the server must wrap the data object
14139  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14140  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14141  * depending on whether the callback name was passed:
14142  * <p>
14143  * <pre><code>
14144 boolean scriptTag = false;
14145 String cb = request.getParameter("callback");
14146 if (cb != null) {
14147     scriptTag = true;
14148     response.setContentType("text/javascript");
14149 } else {
14150     response.setContentType("application/x-json");
14151 }
14152 Writer out = response.getWriter();
14153 if (scriptTag) {
14154     out.write(cb + "(");
14155 }
14156 out.print(dataBlock.toJsonString());
14157 if (scriptTag) {
14158     out.write(");");
14159 }
14160 </pre></code>
14161  *
14162  * @constructor
14163  * @param {Object} config A configuration object.
14164  */
14165 Roo.data.ScriptTagProxy = function(config){
14166     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14167     Roo.apply(this, config);
14168     this.head = document.getElementsByTagName("head")[0];
14169 };
14170
14171 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14172
14173 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14174     /**
14175      * @cfg {String} url The URL from which to request the data object.
14176      */
14177     /**
14178      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14179      */
14180     timeout : 30000,
14181     /**
14182      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14183      * the server the name of the callback function set up by the load call to process the returned data object.
14184      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14185      * javascript output which calls this named function passing the data object as its only parameter.
14186      */
14187     callbackParam : "callback",
14188     /**
14189      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14190      * name to the request.
14191      */
14192     nocache : true,
14193
14194     /**
14195      * Load data from the configured URL, read the data object into
14196      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14197      * process that block using the passed callback.
14198      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14199      * for the request to the remote server.
14200      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14201      * object into a block of Roo.data.Records.
14202      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14203      * The function must be passed <ul>
14204      * <li>The Record block object</li>
14205      * <li>The "arg" argument from the load function</li>
14206      * <li>A boolean success indicator</li>
14207      * </ul>
14208      * @param {Object} scope The scope in which to call the callback
14209      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14210      */
14211     load : function(params, reader, callback, scope, arg){
14212         if(this.fireEvent("beforeload", this, params) !== false){
14213
14214             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14215
14216             var url = this.url;
14217             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14218             if(this.nocache){
14219                 url += "&_dc=" + (new Date().getTime());
14220             }
14221             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14222             var trans = {
14223                 id : transId,
14224                 cb : "stcCallback"+transId,
14225                 scriptId : "stcScript"+transId,
14226                 params : params,
14227                 arg : arg,
14228                 url : url,
14229                 callback : callback,
14230                 scope : scope,
14231                 reader : reader
14232             };
14233             var conn = this;
14234
14235             window[trans.cb] = function(o){
14236                 conn.handleResponse(o, trans);
14237             };
14238
14239             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14240
14241             if(this.autoAbort !== false){
14242                 this.abort();
14243             }
14244
14245             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14246
14247             var script = document.createElement("script");
14248             script.setAttribute("src", url);
14249             script.setAttribute("type", "text/javascript");
14250             script.setAttribute("id", trans.scriptId);
14251             this.head.appendChild(script);
14252
14253             this.trans = trans;
14254         }else{
14255             callback.call(scope||this, null, arg, false);
14256         }
14257     },
14258
14259     // private
14260     isLoading : function(){
14261         return this.trans ? true : false;
14262     },
14263
14264     /**
14265      * Abort the current server request.
14266      */
14267     abort : function(){
14268         if(this.isLoading()){
14269             this.destroyTrans(this.trans);
14270         }
14271     },
14272
14273     // private
14274     destroyTrans : function(trans, isLoaded){
14275         this.head.removeChild(document.getElementById(trans.scriptId));
14276         clearTimeout(trans.timeoutId);
14277         if(isLoaded){
14278             window[trans.cb] = undefined;
14279             try{
14280                 delete window[trans.cb];
14281             }catch(e){}
14282         }else{
14283             // if hasn't been loaded, wait for load to remove it to prevent script error
14284             window[trans.cb] = function(){
14285                 window[trans.cb] = undefined;
14286                 try{
14287                     delete window[trans.cb];
14288                 }catch(e){}
14289             };
14290         }
14291     },
14292
14293     // private
14294     handleResponse : function(o, trans){
14295         this.trans = false;
14296         this.destroyTrans(trans, true);
14297         var result;
14298         try {
14299             result = trans.reader.readRecords(o);
14300         }catch(e){
14301             this.fireEvent("loadexception", this, o, trans.arg, e);
14302             trans.callback.call(trans.scope||window, null, trans.arg, false);
14303             return;
14304         }
14305         this.fireEvent("load", this, o, trans.arg);
14306         trans.callback.call(trans.scope||window, result, trans.arg, true);
14307     },
14308
14309     // private
14310     handleFailure : function(trans){
14311         this.trans = false;
14312         this.destroyTrans(trans, false);
14313         this.fireEvent("loadexception", this, null, trans.arg);
14314         trans.callback.call(trans.scope||window, null, trans.arg, false);
14315     }
14316 });/*
14317  * Based on:
14318  * Ext JS Library 1.1.1
14319  * Copyright(c) 2006-2007, Ext JS, LLC.
14320  *
14321  * Originally Released Under LGPL - original licence link has changed is not relivant.
14322  *
14323  * Fork - LGPL
14324  * <script type="text/javascript">
14325  */
14326
14327 /**
14328  * @class Roo.data.JsonReader
14329  * @extends Roo.data.DataReader
14330  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14331  * based on mappings in a provided Roo.data.Record constructor.
14332  * 
14333  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14334  * in the reply previously. 
14335  * 
14336  * <p>
14337  * Example code:
14338  * <pre><code>
14339 var RecordDef = Roo.data.Record.create([
14340     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14341     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14342 ]);
14343 var myReader = new Roo.data.JsonReader({
14344     totalProperty: "results",    // The property which contains the total dataset size (optional)
14345     root: "rows",                // The property which contains an Array of row objects
14346     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14347 }, RecordDef);
14348 </code></pre>
14349  * <p>
14350  * This would consume a JSON file like this:
14351  * <pre><code>
14352 { 'results': 2, 'rows': [
14353     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14354     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14355 }
14356 </code></pre>
14357  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14358  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14359  * paged from the remote server.
14360  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14361  * @cfg {String} root name of the property which contains the Array of row objects.
14362  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14363  * @cfg {Array} fields Array of field definition objects
14364  * @constructor
14365  * Create a new JsonReader
14366  * @param {Object} meta Metadata configuration options
14367  * @param {Object} recordType Either an Array of field definition objects,
14368  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14369  */
14370 Roo.data.JsonReader = function(meta, recordType){
14371     
14372     meta = meta || {};
14373     // set some defaults:
14374     Roo.applyIf(meta, {
14375         totalProperty: 'total',
14376         successProperty : 'success',
14377         root : 'data',
14378         id : 'id'
14379     });
14380     
14381     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14382 };
14383 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14384     
14385     readerType : 'Json',
14386     
14387     /**
14388      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14389      * Used by Store query builder to append _requestMeta to params.
14390      * 
14391      */
14392     metaFromRemote : false,
14393     /**
14394      * This method is only used by a DataProxy which has retrieved data from a remote server.
14395      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14396      * @return {Object} data A data block which is used by an Roo.data.Store object as
14397      * a cache of Roo.data.Records.
14398      */
14399     read : function(response){
14400         var json = response.responseText;
14401        
14402         var o = /* eval:var:o */ eval("("+json+")");
14403         if(!o) {
14404             throw {message: "JsonReader.read: Json object not found"};
14405         }
14406         
14407         if(o.metaData){
14408             
14409             delete this.ef;
14410             this.metaFromRemote = true;
14411             this.meta = o.metaData;
14412             this.recordType = Roo.data.Record.create(o.metaData.fields);
14413             this.onMetaChange(this.meta, this.recordType, o);
14414         }
14415         return this.readRecords(o);
14416     },
14417
14418     // private function a store will implement
14419     onMetaChange : function(meta, recordType, o){
14420
14421     },
14422
14423     /**
14424          * @ignore
14425          */
14426     simpleAccess: function(obj, subsc) {
14427         return obj[subsc];
14428     },
14429
14430         /**
14431          * @ignore
14432          */
14433     getJsonAccessor: function(){
14434         var re = /[\[\.]/;
14435         return function(expr) {
14436             try {
14437                 return(re.test(expr))
14438                     ? new Function("obj", "return obj." + expr)
14439                     : function(obj){
14440                         return obj[expr];
14441                     };
14442             } catch(e){}
14443             return Roo.emptyFn;
14444         };
14445     }(),
14446
14447     /**
14448      * Create a data block containing Roo.data.Records from an XML document.
14449      * @param {Object} o An object which contains an Array of row objects in the property specified
14450      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14451      * which contains the total size of the dataset.
14452      * @return {Object} data A data block which is used by an Roo.data.Store object as
14453      * a cache of Roo.data.Records.
14454      */
14455     readRecords : function(o){
14456         /**
14457          * After any data loads, the raw JSON data is available for further custom processing.
14458          * @type Object
14459          */
14460         this.o = o;
14461         var s = this.meta, Record = this.recordType,
14462             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14463
14464 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14465         if (!this.ef) {
14466             if(s.totalProperty) {
14467                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14468                 }
14469                 if(s.successProperty) {
14470                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14471                 }
14472                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14473                 if (s.id) {
14474                         var g = this.getJsonAccessor(s.id);
14475                         this.getId = function(rec) {
14476                                 var r = g(rec);  
14477                                 return (r === undefined || r === "") ? null : r;
14478                         };
14479                 } else {
14480                         this.getId = function(){return null;};
14481                 }
14482             this.ef = [];
14483             for(var jj = 0; jj < fl; jj++){
14484                 f = fi[jj];
14485                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14486                 this.ef[jj] = this.getJsonAccessor(map);
14487             }
14488         }
14489
14490         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14491         if(s.totalProperty){
14492             var vt = parseInt(this.getTotal(o), 10);
14493             if(!isNaN(vt)){
14494                 totalRecords = vt;
14495             }
14496         }
14497         if(s.successProperty){
14498             var vs = this.getSuccess(o);
14499             if(vs === false || vs === 'false'){
14500                 success = false;
14501             }
14502         }
14503         var records = [];
14504         for(var i = 0; i < c; i++){
14505                 var n = root[i];
14506             var values = {};
14507             var id = this.getId(n);
14508             for(var j = 0; j < fl; j++){
14509                 f = fi[j];
14510             var v = this.ef[j](n);
14511             if (!f.convert) {
14512                 Roo.log('missing convert for ' + f.name);
14513                 Roo.log(f);
14514                 continue;
14515             }
14516             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14517             }
14518             var record = new Record(values, id);
14519             record.json = n;
14520             records[i] = record;
14521         }
14522         return {
14523             raw : o,
14524             success : success,
14525             records : records,
14526             totalRecords : totalRecords
14527         };
14528     },
14529     // used when loading children.. @see loadDataFromChildren
14530     toLoadData: function(rec)
14531     {
14532         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14533         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14534         return { data : data, total : data.length };
14535         
14536     }
14537 });/*
14538  * Based on:
14539  * Ext JS Library 1.1.1
14540  * Copyright(c) 2006-2007, Ext JS, LLC.
14541  *
14542  * Originally Released Under LGPL - original licence link has changed is not relivant.
14543  *
14544  * Fork - LGPL
14545  * <script type="text/javascript">
14546  */
14547
14548 /**
14549  * @class Roo.data.ArrayReader
14550  * @extends Roo.data.DataReader
14551  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14552  * Each element of that Array represents a row of data fields. The
14553  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14554  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14555  * <p>
14556  * Example code:.
14557  * <pre><code>
14558 var RecordDef = Roo.data.Record.create([
14559     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14560     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14561 ]);
14562 var myReader = new Roo.data.ArrayReader({
14563     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14564 }, RecordDef);
14565 </code></pre>
14566  * <p>
14567  * This would consume an Array like this:
14568  * <pre><code>
14569 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14570   </code></pre>
14571  
14572  * @constructor
14573  * Create a new JsonReader
14574  * @param {Object} meta Metadata configuration options.
14575  * @param {Object|Array} recordType Either an Array of field definition objects
14576  * 
14577  * @cfg {Array} fields Array of field definition objects
14578  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14579  * as specified to {@link Roo.data.Record#create},
14580  * or an {@link Roo.data.Record} object
14581  *
14582  * 
14583  * created using {@link Roo.data.Record#create}.
14584  */
14585 Roo.data.ArrayReader = function(meta, recordType)
14586 {    
14587     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14588 };
14589
14590 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14591     
14592       /**
14593      * Create a data block containing Roo.data.Records from an XML document.
14594      * @param {Object} o An Array of row objects which represents the dataset.
14595      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14596      * a cache of Roo.data.Records.
14597      */
14598     readRecords : function(o)
14599     {
14600         var sid = this.meta ? this.meta.id : null;
14601         var recordType = this.recordType, fields = recordType.prototype.fields;
14602         var records = [];
14603         var root = o;
14604         for(var i = 0; i < root.length; i++){
14605                 var n = root[i];
14606             var values = {};
14607             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14608             for(var j = 0, jlen = fields.length; j < jlen; j++){
14609                 var f = fields.items[j];
14610                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14611                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14612                 v = f.convert(v);
14613                 values[f.name] = v;
14614             }
14615             var record = new recordType(values, id);
14616             record.json = n;
14617             records[records.length] = record;
14618         }
14619         return {
14620             records : records,
14621             totalRecords : records.length
14622         };
14623     },
14624     // used when loading children.. @see loadDataFromChildren
14625     toLoadData: function(rec)
14626     {
14627         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14628         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14629         
14630     }
14631     
14632     
14633 });/*
14634  * - LGPL
14635  * * 
14636  */
14637
14638 /**
14639  * @class Roo.bootstrap.ComboBox
14640  * @extends Roo.bootstrap.TriggerField
14641  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14642  * @cfg {Boolean} append (true|false) default false
14643  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14644  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14645  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14646  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14647  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14648  * @cfg {Boolean} animate default true
14649  * @cfg {Boolean} emptyResultText only for touch device
14650  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14651  * @cfg {String} emptyTitle default ''
14652  * @constructor
14653  * Create a new ComboBox.
14654  * @param {Object} config Configuration options
14655  */
14656 Roo.bootstrap.ComboBox = function(config){
14657     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14658     this.addEvents({
14659         /**
14660          * @event expand
14661          * Fires when the dropdown list is expanded
14662         * @param {Roo.bootstrap.ComboBox} combo This combo box
14663         */
14664         'expand' : true,
14665         /**
14666          * @event collapse
14667          * Fires when the dropdown list is collapsed
14668         * @param {Roo.bootstrap.ComboBox} combo This combo box
14669         */
14670         'collapse' : true,
14671         /**
14672          * @event beforeselect
14673          * Fires before a list item is selected. Return false to cancel the selection.
14674         * @param {Roo.bootstrap.ComboBox} combo This combo box
14675         * @param {Roo.data.Record} record The data record returned from the underlying store
14676         * @param {Number} index The index of the selected item in the dropdown list
14677         */
14678         'beforeselect' : true,
14679         /**
14680          * @event select
14681          * Fires when a list item is selected
14682         * @param {Roo.bootstrap.ComboBox} combo This combo box
14683         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14684         * @param {Number} index The index of the selected item in the dropdown list
14685         */
14686         'select' : true,
14687         /**
14688          * @event beforequery
14689          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14690          * The event object passed has these properties:
14691         * @param {Roo.bootstrap.ComboBox} combo This combo box
14692         * @param {String} query The query
14693         * @param {Boolean} forceAll true to force "all" query
14694         * @param {Boolean} cancel true to cancel the query
14695         * @param {Object} e The query event object
14696         */
14697         'beforequery': true,
14698          /**
14699          * @event add
14700          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14701         * @param {Roo.bootstrap.ComboBox} combo This combo box
14702         */
14703         'add' : true,
14704         /**
14705          * @event edit
14706          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14707         * @param {Roo.bootstrap.ComboBox} combo This combo box
14708         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14709         */
14710         'edit' : true,
14711         /**
14712          * @event remove
14713          * Fires when the remove value from the combobox array
14714         * @param {Roo.bootstrap.ComboBox} combo This combo box
14715         */
14716         'remove' : true,
14717         /**
14718          * @event afterremove
14719          * Fires when the remove value from the combobox array
14720         * @param {Roo.bootstrap.ComboBox} combo This combo box
14721         */
14722         'afterremove' : true,
14723         /**
14724          * @event specialfilter
14725          * Fires when specialfilter
14726             * @param {Roo.bootstrap.ComboBox} combo This combo box
14727             */
14728         'specialfilter' : true,
14729         /**
14730          * @event tick
14731          * Fires when tick the element
14732             * @param {Roo.bootstrap.ComboBox} combo This combo box
14733             */
14734         'tick' : true,
14735         /**
14736          * @event touchviewdisplay
14737          * Fires when touch view require special display (default is using displayField)
14738             * @param {Roo.bootstrap.ComboBox} combo This combo box
14739             * @param {Object} cfg set html .
14740             */
14741         'touchviewdisplay' : true
14742         
14743     });
14744     
14745     this.item = [];
14746     this.tickItems = [];
14747     
14748     this.selectedIndex = -1;
14749     if(this.mode == 'local'){
14750         if(config.queryDelay === undefined){
14751             this.queryDelay = 10;
14752         }
14753         if(config.minChars === undefined){
14754             this.minChars = 0;
14755         }
14756     }
14757 };
14758
14759 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14760      
14761     /**
14762      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14763      * rendering into an Roo.Editor, defaults to false)
14764      */
14765     /**
14766      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14767      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14768      */
14769     /**
14770      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14771      */
14772     /**
14773      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14774      * the dropdown list (defaults to undefined, with no header element)
14775      */
14776
14777      /**
14778      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
14779      */
14780      
14781      /**
14782      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14783      */
14784     listWidth: undefined,
14785     /**
14786      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14787      * mode = 'remote' or 'text' if mode = 'local')
14788      */
14789     displayField: undefined,
14790     
14791     /**
14792      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14793      * mode = 'remote' or 'value' if mode = 'local'). 
14794      * Note: use of a valueField requires the user make a selection
14795      * in order for a value to be mapped.
14796      */
14797     valueField: undefined,
14798     /**
14799      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14800      */
14801     modalTitle : '',
14802     
14803     /**
14804      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14805      * field's data value (defaults to the underlying DOM element's name)
14806      */
14807     hiddenName: undefined,
14808     /**
14809      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14810      */
14811     listClass: '',
14812     /**
14813      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14814      */
14815     selectedClass: 'active',
14816     
14817     /**
14818      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14819      */
14820     shadow:'sides',
14821     /**
14822      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14823      * anchor positions (defaults to 'tl-bl')
14824      */
14825     listAlign: 'tl-bl?',
14826     /**
14827      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14828      */
14829     maxHeight: 300,
14830     /**
14831      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
14832      * query specified by the allQuery config option (defaults to 'query')
14833      */
14834     triggerAction: 'query',
14835     /**
14836      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14837      * (defaults to 4, does not apply if editable = false)
14838      */
14839     minChars : 4,
14840     /**
14841      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14842      * delay (typeAheadDelay) if it matches a known value (defaults to false)
14843      */
14844     typeAhead: false,
14845     /**
14846      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14847      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14848      */
14849     queryDelay: 500,
14850     /**
14851      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14852      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
14853      */
14854     pageSize: 0,
14855     /**
14856      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
14857      * when editable = true (defaults to false)
14858      */
14859     selectOnFocus:false,
14860     /**
14861      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14862      */
14863     queryParam: 'query',
14864     /**
14865      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
14866      * when mode = 'remote' (defaults to 'Loading...')
14867      */
14868     loadingText: 'Loading...',
14869     /**
14870      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14871      */
14872     resizable: false,
14873     /**
14874      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14875      */
14876     handleHeight : 8,
14877     /**
14878      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14879      * traditional select (defaults to true)
14880      */
14881     editable: true,
14882     /**
14883      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14884      */
14885     allQuery: '',
14886     /**
14887      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
14888      */
14889     mode: 'remote',
14890     /**
14891      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
14892      * listWidth has a higher value)
14893      */
14894     minListWidth : 70,
14895     /**
14896      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
14897      * allow the user to set arbitrary text into the field (defaults to false)
14898      */
14899     forceSelection:false,
14900     /**
14901      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
14902      * if typeAhead = true (defaults to 250)
14903      */
14904     typeAheadDelay : 250,
14905     /**
14906      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
14907      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
14908      */
14909     valueNotFoundText : undefined,
14910     /**
14911      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
14912      */
14913     blockFocus : false,
14914     
14915     /**
14916      * @cfg {Boolean} disableClear Disable showing of clear button.
14917      */
14918     disableClear : false,
14919     /**
14920      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
14921      */
14922     alwaysQuery : false,
14923     
14924     /**
14925      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
14926      */
14927     multiple : false,
14928     
14929     /**
14930      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
14931      */
14932     invalidClass : "has-warning",
14933     
14934     /**
14935      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
14936      */
14937     validClass : "has-success",
14938     
14939     /**
14940      * @cfg {Boolean} specialFilter (true|false) special filter default false
14941      */
14942     specialFilter : false,
14943     
14944     /**
14945      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
14946      */
14947     mobileTouchView : true,
14948     
14949     /**
14950      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
14951      */
14952     useNativeIOS : false,
14953     
14954     /**
14955      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
14956      */
14957     mobile_restrict_height : false,
14958     
14959     ios_options : false,
14960     
14961     //private
14962     addicon : false,
14963     editicon: false,
14964     
14965     page: 0,
14966     hasQuery: false,
14967     append: false,
14968     loadNext: false,
14969     autoFocus : true,
14970     tickable : false,
14971     btnPosition : 'right',
14972     triggerList : true,
14973     showToggleBtn : true,
14974     animate : true,
14975     emptyResultText: 'Empty',
14976     triggerText : 'Select',
14977     emptyTitle : '',
14978     
14979     // element that contains real text value.. (when hidden is used..)
14980     
14981     getAutoCreate : function()
14982     {   
14983         var cfg = false;
14984         //render
14985         /*
14986          * Render classic select for iso
14987          */
14988         
14989         if(Roo.isIOS && this.useNativeIOS){
14990             cfg = this.getAutoCreateNativeIOS();
14991             return cfg;
14992         }
14993         
14994         /*
14995          * Touch Devices
14996          */
14997         
14998         if(Roo.isTouch && this.mobileTouchView){
14999             cfg = this.getAutoCreateTouchView();
15000             return cfg;;
15001         }
15002         
15003         /*
15004          *  Normal ComboBox
15005          */
15006         if(!this.tickable){
15007             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15008             return cfg;
15009         }
15010         
15011         /*
15012          *  ComboBox with tickable selections
15013          */
15014              
15015         var align = this.labelAlign || this.parentLabelAlign();
15016         
15017         cfg = {
15018             cls : 'form-group roo-combobox-tickable' //input-group
15019         };
15020         
15021         var btn_text_select = '';
15022         var btn_text_done = '';
15023         var btn_text_cancel = '';
15024         
15025         if (this.btn_text_show) {
15026             btn_text_select = 'Select';
15027             btn_text_done = 'Done';
15028             btn_text_cancel = 'Cancel'; 
15029         }
15030         
15031         var buttons = {
15032             tag : 'div',
15033             cls : 'tickable-buttons',
15034             cn : [
15035                 {
15036                     tag : 'button',
15037                     type : 'button',
15038                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15039                     //html : this.triggerText
15040                     html: btn_text_select
15041                 },
15042                 {
15043                     tag : 'button',
15044                     type : 'button',
15045                     name : 'ok',
15046                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15047                     //html : 'Done'
15048                     html: btn_text_done
15049                 },
15050                 {
15051                     tag : 'button',
15052                     type : 'button',
15053                     name : 'cancel',
15054                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15055                     //html : 'Cancel'
15056                     html: btn_text_cancel
15057                 }
15058             ]
15059         };
15060         
15061         if(this.editable){
15062             buttons.cn.unshift({
15063                 tag: 'input',
15064                 cls: 'roo-select2-search-field-input'
15065             });
15066         }
15067         
15068         var _this = this;
15069         
15070         Roo.each(buttons.cn, function(c){
15071             if (_this.size) {
15072                 c.cls += ' btn-' + _this.size;
15073             }
15074
15075             if (_this.disabled) {
15076                 c.disabled = true;
15077             }
15078         });
15079         
15080         var box = {
15081             tag: 'div',
15082             style : 'display: contents',
15083             cn: [
15084                 {
15085                     tag: 'input',
15086                     type : 'hidden',
15087                     cls: 'form-hidden-field'
15088                 },
15089                 {
15090                     tag: 'ul',
15091                     cls: 'roo-select2-choices',
15092                     cn:[
15093                         {
15094                             tag: 'li',
15095                             cls: 'roo-select2-search-field',
15096                             cn: [
15097                                 buttons
15098                             ]
15099                         }
15100                     ]
15101                 }
15102             ]
15103         };
15104         
15105         var combobox = {
15106             cls: 'roo-select2-container input-group roo-select2-container-multi',
15107             cn: [
15108                 
15109                 box
15110 //                {
15111 //                    tag: 'ul',
15112 //                    cls: 'typeahead typeahead-long dropdown-menu',
15113 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15114 //                }
15115             ]
15116         };
15117         
15118         if(this.hasFeedback && !this.allowBlank){
15119             
15120             var feedback = {
15121                 tag: 'span',
15122                 cls: 'glyphicon form-control-feedback'
15123             };
15124
15125             combobox.cn.push(feedback);
15126         }
15127         
15128         
15129         
15130         var indicator = {
15131             tag : 'i',
15132             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15133             tooltip : 'This field is required'
15134         };
15135         if (Roo.bootstrap.version == 4) {
15136             indicator = {
15137                 tag : 'i',
15138                 style : 'display:none'
15139             };
15140         }
15141         if (align ==='left' && this.fieldLabel.length) {
15142             
15143             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15144             
15145             cfg.cn = [
15146                 indicator,
15147                 {
15148                     tag: 'label',
15149                     'for' :  id,
15150                     cls : 'control-label col-form-label',
15151                     html : this.fieldLabel
15152
15153                 },
15154                 {
15155                     cls : "", 
15156                     cn: [
15157                         combobox
15158                     ]
15159                 }
15160
15161             ];
15162             
15163             var labelCfg = cfg.cn[1];
15164             var contentCfg = cfg.cn[2];
15165             
15166
15167             if(this.indicatorpos == 'right'){
15168                 
15169                 cfg.cn = [
15170                     {
15171                         tag: 'label',
15172                         'for' :  id,
15173                         cls : 'control-label col-form-label',
15174                         cn : [
15175                             {
15176                                 tag : 'span',
15177                                 html : this.fieldLabel
15178                             },
15179                             indicator
15180                         ]
15181                     },
15182                     {
15183                         cls : "",
15184                         cn: [
15185                             combobox
15186                         ]
15187                     }
15188
15189                 ];
15190                 
15191                 
15192                 
15193                 labelCfg = cfg.cn[0];
15194                 contentCfg = cfg.cn[1];
15195             
15196             }
15197             
15198             if(this.labelWidth > 12){
15199                 labelCfg.style = "width: " + this.labelWidth + 'px';
15200             }
15201             
15202             if(this.labelWidth < 13 && this.labelmd == 0){
15203                 this.labelmd = this.labelWidth;
15204             }
15205             
15206             if(this.labellg > 0){
15207                 labelCfg.cls += ' col-lg-' + this.labellg;
15208                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15209             }
15210             
15211             if(this.labelmd > 0){
15212                 labelCfg.cls += ' col-md-' + this.labelmd;
15213                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15214             }
15215             
15216             if(this.labelsm > 0){
15217                 labelCfg.cls += ' col-sm-' + this.labelsm;
15218                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15219             }
15220             
15221             if(this.labelxs > 0){
15222                 labelCfg.cls += ' col-xs-' + this.labelxs;
15223                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15224             }
15225                 
15226                 
15227         } else if ( this.fieldLabel.length) {
15228 //                Roo.log(" label");
15229                  cfg.cn = [
15230                    indicator,
15231                     {
15232                         tag: 'label',
15233                         //cls : 'input-group-addon',
15234                         html : this.fieldLabel
15235                     },
15236                     combobox
15237                 ];
15238                 
15239                 if(this.indicatorpos == 'right'){
15240                     cfg.cn = [
15241                         {
15242                             tag: 'label',
15243                             //cls : 'input-group-addon',
15244                             html : this.fieldLabel
15245                         },
15246                         indicator,
15247                         combobox
15248                     ];
15249                     
15250                 }
15251
15252         } else {
15253             
15254 //                Roo.log(" no label && no align");
15255                 cfg = combobox
15256                      
15257                 
15258         }
15259          
15260         var settings=this;
15261         ['xs','sm','md','lg'].map(function(size){
15262             if (settings[size]) {
15263                 cfg.cls += ' col-' + size + '-' + settings[size];
15264             }
15265         });
15266         
15267         return cfg;
15268         
15269     },
15270     
15271     _initEventsCalled : false,
15272     
15273     // private
15274     initEvents: function()
15275     {   
15276         if (this._initEventsCalled) { // as we call render... prevent looping...
15277             return;
15278         }
15279         this._initEventsCalled = true;
15280         
15281         if (!this.store) {
15282             throw "can not find store for combo";
15283         }
15284         
15285         this.indicator = this.indicatorEl();
15286         
15287         this.store = Roo.factory(this.store, Roo.data);
15288         this.store.parent = this;
15289         
15290         // if we are building from html. then this element is so complex, that we can not really
15291         // use the rendered HTML.
15292         // so we have to trash and replace the previous code.
15293         if (Roo.XComponent.build_from_html) {
15294             // remove this element....
15295             var e = this.el.dom, k=0;
15296             while (e ) { e = e.previousSibling;  ++k;}
15297
15298             this.el.remove();
15299             
15300             this.el=false;
15301             this.rendered = false;
15302             
15303             this.render(this.parent().getChildContainer(true), k);
15304         }
15305         
15306         if(Roo.isIOS && this.useNativeIOS){
15307             this.initIOSView();
15308             return;
15309         }
15310         
15311         /*
15312          * Touch Devices
15313          */
15314         
15315         if(Roo.isTouch && this.mobileTouchView){
15316             this.initTouchView();
15317             return;
15318         }
15319         
15320         if(this.tickable){
15321             this.initTickableEvents();
15322             return;
15323         }
15324         
15325         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15326         
15327         if(this.hiddenName){
15328             
15329             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15330             
15331             this.hiddenField.dom.value =
15332                 this.hiddenValue !== undefined ? this.hiddenValue :
15333                 this.value !== undefined ? this.value : '';
15334
15335             // prevent input submission
15336             this.el.dom.removeAttribute('name');
15337             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15338              
15339              
15340         }
15341         //if(Roo.isGecko){
15342         //    this.el.dom.setAttribute('autocomplete', 'off');
15343         //}
15344         
15345         var cls = 'x-combo-list';
15346         
15347         //this.list = new Roo.Layer({
15348         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15349         //});
15350         
15351         var _this = this;
15352         
15353         (function(){
15354             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15355             _this.list.setWidth(lw);
15356         }).defer(100);
15357         
15358         this.list.on('mouseover', this.onViewOver, this);
15359         this.list.on('mousemove', this.onViewMove, this);
15360         this.list.on('scroll', this.onViewScroll, this);
15361         
15362         /*
15363         this.list.swallowEvent('mousewheel');
15364         this.assetHeight = 0;
15365
15366         if(this.title){
15367             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15368             this.assetHeight += this.header.getHeight();
15369         }
15370
15371         this.innerList = this.list.createChild({cls:cls+'-inner'});
15372         this.innerList.on('mouseover', this.onViewOver, this);
15373         this.innerList.on('mousemove', this.onViewMove, this);
15374         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15375         
15376         if(this.allowBlank && !this.pageSize && !this.disableClear){
15377             this.footer = this.list.createChild({cls:cls+'-ft'});
15378             this.pageTb = new Roo.Toolbar(this.footer);
15379            
15380         }
15381         if(this.pageSize){
15382             this.footer = this.list.createChild({cls:cls+'-ft'});
15383             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15384                     {pageSize: this.pageSize});
15385             
15386         }
15387         
15388         if (this.pageTb && this.allowBlank && !this.disableClear) {
15389             var _this = this;
15390             this.pageTb.add(new Roo.Toolbar.Fill(), {
15391                 cls: 'x-btn-icon x-btn-clear',
15392                 text: '&#160;',
15393                 handler: function()
15394                 {
15395                     _this.collapse();
15396                     _this.clearValue();
15397                     _this.onSelect(false, -1);
15398                 }
15399             });
15400         }
15401         if (this.footer) {
15402             this.assetHeight += this.footer.getHeight();
15403         }
15404         */
15405             
15406         if(!this.tpl){
15407             this.tpl = Roo.bootstrap.version == 4 ?
15408                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15409                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15410         }
15411
15412         this.view = new Roo.View(this.list, this.tpl, {
15413             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15414         });
15415         //this.view.wrapEl.setDisplayed(false);
15416         this.view.on('click', this.onViewClick, this);
15417         
15418         
15419         this.store.on('beforeload', this.onBeforeLoad, this);
15420         this.store.on('load', this.onLoad, this);
15421         this.store.on('loadexception', this.onLoadException, this);
15422         /*
15423         if(this.resizable){
15424             this.resizer = new Roo.Resizable(this.list,  {
15425                pinned:true, handles:'se'
15426             });
15427             this.resizer.on('resize', function(r, w, h){
15428                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15429                 this.listWidth = w;
15430                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15431                 this.restrictHeight();
15432             }, this);
15433             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15434         }
15435         */
15436         if(!this.editable){
15437             this.editable = true;
15438             this.setEditable(false);
15439         }
15440         
15441         /*
15442         
15443         if (typeof(this.events.add.listeners) != 'undefined') {
15444             
15445             this.addicon = this.wrap.createChild(
15446                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15447        
15448             this.addicon.on('click', function(e) {
15449                 this.fireEvent('add', this);
15450             }, this);
15451         }
15452         if (typeof(this.events.edit.listeners) != 'undefined') {
15453             
15454             this.editicon = this.wrap.createChild(
15455                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15456             if (this.addicon) {
15457                 this.editicon.setStyle('margin-left', '40px');
15458             }
15459             this.editicon.on('click', function(e) {
15460                 
15461                 // we fire even  if inothing is selected..
15462                 this.fireEvent('edit', this, this.lastData );
15463                 
15464             }, this);
15465         }
15466         */
15467         
15468         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15469             "up" : function(e){
15470                 this.inKeyMode = true;
15471                 this.selectPrev();
15472             },
15473
15474             "down" : function(e){
15475                 if(!this.isExpanded()){
15476                     this.onTriggerClick();
15477                 }else{
15478                     this.inKeyMode = true;
15479                     this.selectNext();
15480                 }
15481             },
15482
15483             "enter" : function(e){
15484 //                this.onViewClick();
15485                 //return true;
15486                 this.collapse();
15487                 
15488                 if(this.fireEvent("specialkey", this, e)){
15489                     this.onViewClick(false);
15490                 }
15491                 
15492                 return true;
15493             },
15494
15495             "esc" : function(e){
15496                 this.collapse();
15497             },
15498
15499             "tab" : function(e){
15500                 this.collapse();
15501                 
15502                 if(this.fireEvent("specialkey", this, e)){
15503                     this.onViewClick(false);
15504                 }
15505                 
15506                 return true;
15507             },
15508
15509             scope : this,
15510
15511             doRelay : function(foo, bar, hname){
15512                 if(hname == 'down' || this.scope.isExpanded()){
15513                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15514                 }
15515                 return true;
15516             },
15517
15518             forceKeyDown: true
15519         });
15520         
15521         
15522         this.queryDelay = Math.max(this.queryDelay || 10,
15523                 this.mode == 'local' ? 10 : 250);
15524         
15525         
15526         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15527         
15528         if(this.typeAhead){
15529             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15530         }
15531         if(this.editable !== false){
15532             this.inputEl().on("keyup", this.onKeyUp, this);
15533         }
15534         if(this.forceSelection){
15535             this.inputEl().on('blur', this.doForce, this);
15536         }
15537         
15538         if(this.multiple){
15539             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15540             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15541         }
15542     },
15543     
15544     initTickableEvents: function()
15545     {   
15546         this.createList();
15547         
15548         if(this.hiddenName){
15549             
15550             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15551             
15552             this.hiddenField.dom.value =
15553                 this.hiddenValue !== undefined ? this.hiddenValue :
15554                 this.value !== undefined ? this.value : '';
15555
15556             // prevent input submission
15557             this.el.dom.removeAttribute('name');
15558             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15559              
15560              
15561         }
15562         
15563 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15564         
15565         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15566         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15567         if(this.triggerList){
15568             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15569         }
15570          
15571         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15572         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15573         
15574         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15575         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15576         
15577         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15578         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15579         
15580         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15581         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15582         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15583         
15584         this.okBtn.hide();
15585         this.cancelBtn.hide();
15586         
15587         var _this = this;
15588         
15589         (function(){
15590             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15591             _this.list.setWidth(lw);
15592         }).defer(100);
15593         
15594         this.list.on('mouseover', this.onViewOver, this);
15595         this.list.on('mousemove', this.onViewMove, this);
15596         
15597         this.list.on('scroll', this.onViewScroll, this);
15598         
15599         if(!this.tpl){
15600             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15601                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15602         }
15603
15604         this.view = new Roo.View(this.list, this.tpl, {
15605             singleSelect:true,
15606             tickable:true,
15607             parent:this,
15608             store: this.store,
15609             selectedClass: this.selectedClass
15610         });
15611         
15612         //this.view.wrapEl.setDisplayed(false);
15613         this.view.on('click', this.onViewClick, this);
15614         
15615         
15616         
15617         this.store.on('beforeload', this.onBeforeLoad, this);
15618         this.store.on('load', this.onLoad, this);
15619         this.store.on('loadexception', this.onLoadException, this);
15620         
15621         if(this.editable){
15622             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15623                 "up" : function(e){
15624                     this.inKeyMode = true;
15625                     this.selectPrev();
15626                 },
15627
15628                 "down" : function(e){
15629                     this.inKeyMode = true;
15630                     this.selectNext();
15631                 },
15632
15633                 "enter" : function(e){
15634                     if(this.fireEvent("specialkey", this, e)){
15635                         this.onViewClick(false);
15636                     }
15637                     
15638                     return true;
15639                 },
15640
15641                 "esc" : function(e){
15642                     this.onTickableFooterButtonClick(e, false, false);
15643                 },
15644
15645                 "tab" : function(e){
15646                     this.fireEvent("specialkey", this, e);
15647                     
15648                     this.onTickableFooterButtonClick(e, false, false);
15649                     
15650                     return true;
15651                 },
15652
15653                 scope : this,
15654
15655                 doRelay : function(e, fn, key){
15656                     if(this.scope.isExpanded()){
15657                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15658                     }
15659                     return true;
15660                 },
15661
15662                 forceKeyDown: true
15663             });
15664         }
15665         
15666         this.queryDelay = Math.max(this.queryDelay || 10,
15667                 this.mode == 'local' ? 10 : 250);
15668         
15669         
15670         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15671         
15672         if(this.typeAhead){
15673             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15674         }
15675         
15676         if(this.editable !== false){
15677             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15678         }
15679         
15680         this.indicator = this.indicatorEl();
15681         
15682         if(this.indicator){
15683             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15684             this.indicator.hide();
15685         }
15686         
15687     },
15688
15689     onDestroy : function(){
15690         if(this.view){
15691             this.view.setStore(null);
15692             this.view.el.removeAllListeners();
15693             this.view.el.remove();
15694             this.view.purgeListeners();
15695         }
15696         if(this.list){
15697             this.list.dom.innerHTML  = '';
15698         }
15699         
15700         if(this.store){
15701             this.store.un('beforeload', this.onBeforeLoad, this);
15702             this.store.un('load', this.onLoad, this);
15703             this.store.un('loadexception', this.onLoadException, this);
15704         }
15705         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15706     },
15707
15708     // private
15709     fireKey : function(e){
15710         if(e.isNavKeyPress() && !this.list.isVisible()){
15711             this.fireEvent("specialkey", this, e);
15712         }
15713     },
15714
15715     // private
15716     onResize: function(w, h){
15717 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15718 //        
15719 //        if(typeof w != 'number'){
15720 //            // we do not handle it!?!?
15721 //            return;
15722 //        }
15723 //        var tw = this.trigger.getWidth();
15724 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15725 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15726 //        var x = w - tw;
15727 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15728 //            
15729 //        //this.trigger.setStyle('left', x+'px');
15730 //        
15731 //        if(this.list && this.listWidth === undefined){
15732 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15733 //            this.list.setWidth(lw);
15734 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15735 //        }
15736         
15737     
15738         
15739     },
15740
15741     /**
15742      * Allow or prevent the user from directly editing the field text.  If false is passed,
15743      * the user will only be able to select from the items defined in the dropdown list.  This method
15744      * is the runtime equivalent of setting the 'editable' config option at config time.
15745      * @param {Boolean} value True to allow the user to directly edit the field text
15746      */
15747     setEditable : function(value){
15748         if(value == this.editable){
15749             return;
15750         }
15751         this.editable = value;
15752         if(!value){
15753             this.inputEl().dom.setAttribute('readOnly', true);
15754             this.inputEl().on('mousedown', this.onTriggerClick,  this);
15755             this.inputEl().addClass('x-combo-noedit');
15756         }else{
15757             this.inputEl().dom.setAttribute('readOnly', false);
15758             this.inputEl().un('mousedown', this.onTriggerClick,  this);
15759             this.inputEl().removeClass('x-combo-noedit');
15760         }
15761     },
15762
15763     // private
15764     
15765     onBeforeLoad : function(combo,opts){
15766         if(!this.hasFocus){
15767             return;
15768         }
15769          if (!opts.add) {
15770             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15771          }
15772         this.restrictHeight();
15773         this.selectedIndex = -1;
15774     },
15775
15776     // private
15777     onLoad : function(){
15778         
15779         this.hasQuery = false;
15780         
15781         if(!this.hasFocus){
15782             return;
15783         }
15784         
15785         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15786             this.loading.hide();
15787         }
15788         
15789         if(this.store.getCount() > 0){
15790             
15791             this.expand();
15792             this.restrictHeight();
15793             if(this.lastQuery == this.allQuery){
15794                 if(this.editable && !this.tickable){
15795                     this.inputEl().dom.select();
15796                 }
15797                 
15798                 if(
15799                     !this.selectByValue(this.value, true) &&
15800                     this.autoFocus && 
15801                     (
15802                         !this.store.lastOptions ||
15803                         typeof(this.store.lastOptions.add) == 'undefined' || 
15804                         this.store.lastOptions.add != true
15805                     )
15806                 ){
15807                     this.select(0, true);
15808                 }
15809             }else{
15810                 if(this.autoFocus){
15811                     this.selectNext();
15812                 }
15813                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15814                     this.taTask.delay(this.typeAheadDelay);
15815                 }
15816             }
15817         }else{
15818             this.onEmptyResults();
15819         }
15820         
15821         //this.el.focus();
15822     },
15823     // private
15824     onLoadException : function()
15825     {
15826         this.hasQuery = false;
15827         
15828         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15829             this.loading.hide();
15830         }
15831         
15832         if(this.tickable && this.editable){
15833             return;
15834         }
15835         
15836         this.collapse();
15837         // only causes errors at present
15838         //Roo.log(this.store.reader.jsonData);
15839         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15840             // fixme
15841             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15842         //}
15843         
15844         
15845     },
15846     // private
15847     onTypeAhead : function(){
15848         if(this.store.getCount() > 0){
15849             var r = this.store.getAt(0);
15850             var newValue = r.data[this.displayField];
15851             var len = newValue.length;
15852             var selStart = this.getRawValue().length;
15853             
15854             if(selStart != len){
15855                 this.setRawValue(newValue);
15856                 this.selectText(selStart, newValue.length);
15857             }
15858         }
15859     },
15860
15861     // private
15862     onSelect : function(record, index){
15863         
15864         if(this.fireEvent('beforeselect', this, record, index) !== false){
15865         
15866             this.setFromData(index > -1 ? record.data : false);
15867             
15868             this.collapse();
15869             this.fireEvent('select', this, record, index);
15870         }
15871     },
15872
15873     /**
15874      * Returns the currently selected field value or empty string if no value is set.
15875      * @return {String} value The selected value
15876      */
15877     getValue : function()
15878     {
15879         if(Roo.isIOS && this.useNativeIOS){
15880             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15881         }
15882         
15883         if(this.multiple){
15884             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
15885         }
15886         
15887         if(this.valueField){
15888             return typeof this.value != 'undefined' ? this.value : '';
15889         }else{
15890             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
15891         }
15892     },
15893     
15894     getRawValue : function()
15895     {
15896         if(Roo.isIOS && this.useNativeIOS){
15897             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
15898         }
15899         
15900         var v = this.inputEl().getValue();
15901         
15902         return v;
15903     },
15904
15905     /**
15906      * Clears any text/value currently set in the field
15907      */
15908     clearValue : function(){
15909         
15910         if(this.hiddenField){
15911             this.hiddenField.dom.value = '';
15912         }
15913         this.value = '';
15914         this.setRawValue('');
15915         this.lastSelectionText = '';
15916         this.lastData = false;
15917         
15918         var close = this.closeTriggerEl();
15919         
15920         if(close){
15921             close.hide();
15922         }
15923         
15924         this.validate();
15925         
15926     },
15927
15928     /**
15929      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
15930      * will be displayed in the field.  If the value does not match the data value of an existing item,
15931      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
15932      * Otherwise the field will be blank (although the value will still be set).
15933      * @param {String} value The value to match
15934      */
15935     setValue : function(v)
15936     {
15937         if(Roo.isIOS && this.useNativeIOS){
15938             this.setIOSValue(v);
15939             return;
15940         }
15941         
15942         if(this.multiple){
15943             this.syncValue();
15944             return;
15945         }
15946         
15947         var text = v;
15948         if(this.valueField){
15949             var r = this.findRecord(this.valueField, v);
15950             if(r){
15951                 text = r.data[this.displayField];
15952             }else if(this.valueNotFoundText !== undefined){
15953                 text = this.valueNotFoundText;
15954             }
15955         }
15956         this.lastSelectionText = text;
15957         if(this.hiddenField){
15958             this.hiddenField.dom.value = v;
15959         }
15960         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
15961         this.value = v;
15962         
15963         var close = this.closeTriggerEl();
15964         
15965         if(close){
15966             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
15967         }
15968         
15969         this.validate();
15970     },
15971     /**
15972      * @property {Object} the last set data for the element
15973      */
15974     
15975     lastData : false,
15976     /**
15977      * Sets the value of the field based on a object which is related to the record format for the store.
15978      * @param {Object} value the value to set as. or false on reset?
15979      */
15980     setFromData : function(o){
15981         
15982         if(this.multiple){
15983             this.addItem(o);
15984             return;
15985         }
15986             
15987         var dv = ''; // display value
15988         var vv = ''; // value value..
15989         this.lastData = o;
15990         if (this.displayField) {
15991             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15992         } else {
15993             // this is an error condition!!!
15994             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15995         }
15996         
15997         if(this.valueField){
15998             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
15999         }
16000         
16001         var close = this.closeTriggerEl();
16002         
16003         if(close){
16004             if(dv.length || vv * 1 > 0){
16005                 close.show() ;
16006                 this.blockFocus=true;
16007             } else {
16008                 close.hide();
16009             }             
16010         }
16011         
16012         if(this.hiddenField){
16013             this.hiddenField.dom.value = vv;
16014             
16015             this.lastSelectionText = dv;
16016             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16017             this.value = vv;
16018             return;
16019         }
16020         // no hidden field.. - we store the value in 'value', but still display
16021         // display field!!!!
16022         this.lastSelectionText = dv;
16023         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16024         this.value = vv;
16025         
16026         
16027         
16028     },
16029     // private
16030     reset : function(){
16031         // overridden so that last data is reset..
16032         
16033         if(this.multiple){
16034             this.clearItem();
16035             return;
16036         }
16037         
16038         this.setValue(this.originalValue);
16039         //this.clearInvalid();
16040         this.lastData = false;
16041         if (this.view) {
16042             this.view.clearSelections();
16043         }
16044         
16045         this.validate();
16046     },
16047     // private
16048     findRecord : function(prop, value){
16049         var record;
16050         if(this.store.getCount() > 0){
16051             this.store.each(function(r){
16052                 if(r.data[prop] == value){
16053                     record = r;
16054                     return false;
16055                 }
16056                 return true;
16057             });
16058         }
16059         return record;
16060     },
16061     
16062     getName: function()
16063     {
16064         // returns hidden if it's set..
16065         if (!this.rendered) {return ''};
16066         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16067         
16068     },
16069     // private
16070     onViewMove : function(e, t){
16071         this.inKeyMode = false;
16072     },
16073
16074     // private
16075     onViewOver : function(e, t){
16076         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16077             return;
16078         }
16079         var item = this.view.findItemFromChild(t);
16080         
16081         if(item){
16082             var index = this.view.indexOf(item);
16083             this.select(index, false);
16084         }
16085     },
16086
16087     // private
16088     onViewClick : function(view, doFocus, el, e)
16089     {
16090         var index = this.view.getSelectedIndexes()[0];
16091         
16092         var r = this.store.getAt(index);
16093         
16094         if(this.tickable){
16095             
16096             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16097                 return;
16098             }
16099             
16100             var rm = false;
16101             var _this = this;
16102             
16103             Roo.each(this.tickItems, function(v,k){
16104                 
16105                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16106                     Roo.log(v);
16107                     _this.tickItems.splice(k, 1);
16108                     
16109                     if(typeof(e) == 'undefined' && view == false){
16110                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16111                     }
16112                     
16113                     rm = true;
16114                     return;
16115                 }
16116             });
16117             
16118             if(rm){
16119                 return;
16120             }
16121             
16122             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16123                 this.tickItems.push(r.data);
16124             }
16125             
16126             if(typeof(e) == 'undefined' && view == false){
16127                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16128             }
16129                     
16130             return;
16131         }
16132         
16133         if(r){
16134             this.onSelect(r, index);
16135         }
16136         if(doFocus !== false && !this.blockFocus){
16137             this.inputEl().focus();
16138         }
16139     },
16140
16141     // private
16142     restrictHeight : function(){
16143         //this.innerList.dom.style.height = '';
16144         //var inner = this.innerList.dom;
16145         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16146         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16147         //this.list.beginUpdate();
16148         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16149         this.list.alignTo(this.inputEl(), this.listAlign);
16150         this.list.alignTo(this.inputEl(), this.listAlign);
16151         //this.list.endUpdate();
16152     },
16153
16154     // private
16155     onEmptyResults : function(){
16156         
16157         if(this.tickable && this.editable){
16158             this.hasFocus = false;
16159             this.restrictHeight();
16160             return;
16161         }
16162         
16163         this.collapse();
16164     },
16165
16166     /**
16167      * Returns true if the dropdown list is expanded, else false.
16168      */
16169     isExpanded : function(){
16170         return this.list.isVisible();
16171     },
16172
16173     /**
16174      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16175      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16176      * @param {String} value The data value of the item to select
16177      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16178      * selected item if it is not currently in view (defaults to true)
16179      * @return {Boolean} True if the value matched an item in the list, else false
16180      */
16181     selectByValue : function(v, scrollIntoView){
16182         if(v !== undefined && v !== null){
16183             var r = this.findRecord(this.valueField || this.displayField, v);
16184             if(r){
16185                 this.select(this.store.indexOf(r), scrollIntoView);
16186                 return true;
16187             }
16188         }
16189         return false;
16190     },
16191
16192     /**
16193      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16194      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16195      * @param {Number} index The zero-based index of the list item to select
16196      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16197      * selected item if it is not currently in view (defaults to true)
16198      */
16199     select : function(index, scrollIntoView){
16200         this.selectedIndex = index;
16201         this.view.select(index);
16202         if(scrollIntoView !== false){
16203             var el = this.view.getNode(index);
16204             /*
16205              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16206              */
16207             if(el){
16208                 this.list.scrollChildIntoView(el, false);
16209             }
16210         }
16211     },
16212
16213     // private
16214     selectNext : function(){
16215         var ct = this.store.getCount();
16216         if(ct > 0){
16217             if(this.selectedIndex == -1){
16218                 this.select(0);
16219             }else if(this.selectedIndex < ct-1){
16220                 this.select(this.selectedIndex+1);
16221             }
16222         }
16223     },
16224
16225     // private
16226     selectPrev : function(){
16227         var ct = this.store.getCount();
16228         if(ct > 0){
16229             if(this.selectedIndex == -1){
16230                 this.select(0);
16231             }else if(this.selectedIndex != 0){
16232                 this.select(this.selectedIndex-1);
16233             }
16234         }
16235     },
16236
16237     // private
16238     onKeyUp : function(e){
16239         if(this.editable !== false && !e.isSpecialKey()){
16240             this.lastKey = e.getKey();
16241             this.dqTask.delay(this.queryDelay);
16242         }
16243     },
16244
16245     // private
16246     validateBlur : function(){
16247         return !this.list || !this.list.isVisible();   
16248     },
16249
16250     // private
16251     initQuery : function(){
16252         
16253         var v = this.getRawValue();
16254         
16255         if(this.tickable && this.editable){
16256             v = this.tickableInputEl().getValue();
16257         }
16258         
16259         this.doQuery(v);
16260     },
16261
16262     // private
16263     doForce : function(){
16264         if(this.inputEl().dom.value.length > 0){
16265             this.inputEl().dom.value =
16266                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16267              
16268         }
16269     },
16270
16271     /**
16272      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16273      * query allowing the query action to be canceled if needed.
16274      * @param {String} query The SQL query to execute
16275      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16276      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16277      * saved in the current store (defaults to false)
16278      */
16279     doQuery : function(q, forceAll){
16280         
16281         if(q === undefined || q === null){
16282             q = '';
16283         }
16284         var qe = {
16285             query: q,
16286             forceAll: forceAll,
16287             combo: this,
16288             cancel:false
16289         };
16290         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16291             return false;
16292         }
16293         q = qe.query;
16294         
16295         forceAll = qe.forceAll;
16296         if(forceAll === true || (q.length >= this.minChars)){
16297             
16298             this.hasQuery = true;
16299             
16300             if(this.lastQuery != q || this.alwaysQuery){
16301                 this.lastQuery = q;
16302                 if(this.mode == 'local'){
16303                     this.selectedIndex = -1;
16304                     if(forceAll){
16305                         this.store.clearFilter();
16306                     }else{
16307                         
16308                         if(this.specialFilter){
16309                             this.fireEvent('specialfilter', this);
16310                             this.onLoad();
16311                             return;
16312                         }
16313                         
16314                         this.store.filter(this.displayField, q);
16315                     }
16316                     
16317                     this.store.fireEvent("datachanged", this.store);
16318                     
16319                     this.onLoad();
16320                     
16321                     
16322                 }else{
16323                     
16324                     this.store.baseParams[this.queryParam] = q;
16325                     
16326                     var options = {params : this.getParams(q)};
16327                     
16328                     if(this.loadNext){
16329                         options.add = true;
16330                         options.params.start = this.page * this.pageSize;
16331                     }
16332                     
16333                     this.store.load(options);
16334                     
16335                     /*
16336                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16337                      *  we should expand the list on onLoad
16338                      *  so command out it
16339                      */
16340 //                    this.expand();
16341                 }
16342             }else{
16343                 this.selectedIndex = -1;
16344                 this.onLoad();   
16345             }
16346         }
16347         
16348         this.loadNext = false;
16349     },
16350     
16351     // private
16352     getParams : function(q){
16353         var p = {};
16354         //p[this.queryParam] = q;
16355         
16356         if(this.pageSize){
16357             p.start = 0;
16358             p.limit = this.pageSize;
16359         }
16360         return p;
16361     },
16362
16363     /**
16364      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16365      */
16366     collapse : function(){
16367         if(!this.isExpanded()){
16368             return;
16369         }
16370         
16371         this.list.hide();
16372         
16373         this.hasFocus = false;
16374         
16375         if(this.tickable){
16376             this.okBtn.hide();
16377             this.cancelBtn.hide();
16378             this.trigger.show();
16379             
16380             if(this.editable){
16381                 this.tickableInputEl().dom.value = '';
16382                 this.tickableInputEl().blur();
16383             }
16384             
16385         }
16386         
16387         Roo.get(document).un('mousedown', this.collapseIf, this);
16388         Roo.get(document).un('mousewheel', this.collapseIf, this);
16389         if (!this.editable) {
16390             Roo.get(document).un('keydown', this.listKeyPress, this);
16391         }
16392         this.fireEvent('collapse', this);
16393         
16394         this.validate();
16395     },
16396
16397     // private
16398     collapseIf : function(e){
16399         var in_combo  = e.within(this.el);
16400         var in_list =  e.within(this.list);
16401         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16402         
16403         if (in_combo || in_list || is_list) {
16404             //e.stopPropagation();
16405             return;
16406         }
16407         
16408         if(this.tickable){
16409             this.onTickableFooterButtonClick(e, false, false);
16410         }
16411
16412         this.collapse();
16413         
16414     },
16415
16416     /**
16417      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16418      */
16419     expand : function(){
16420        
16421         if(this.isExpanded() || !this.hasFocus){
16422             return;
16423         }
16424         
16425         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16426         this.list.setWidth(lw);
16427         
16428         Roo.log('expand');
16429         
16430         this.list.show();
16431         
16432         this.restrictHeight();
16433         
16434         if(this.tickable){
16435             
16436             this.tickItems = Roo.apply([], this.item);
16437             
16438             this.okBtn.show();
16439             this.cancelBtn.show();
16440             this.trigger.hide();
16441             
16442             if(this.editable){
16443                 this.tickableInputEl().focus();
16444             }
16445             
16446         }
16447         
16448         Roo.get(document).on('mousedown', this.collapseIf, this);
16449         Roo.get(document).on('mousewheel', this.collapseIf, this);
16450         if (!this.editable) {
16451             Roo.get(document).on('keydown', this.listKeyPress, this);
16452         }
16453         
16454         this.fireEvent('expand', this);
16455     },
16456
16457     // private
16458     // Implements the default empty TriggerField.onTriggerClick function
16459     onTriggerClick : function(e)
16460     {
16461         Roo.log('trigger click');
16462         
16463         if(this.disabled || !this.triggerList){
16464             return;
16465         }
16466         
16467         this.page = 0;
16468         this.loadNext = false;
16469         
16470         if(this.isExpanded()){
16471             this.collapse();
16472             if (!this.blockFocus) {
16473                 this.inputEl().focus();
16474             }
16475             
16476         }else {
16477             this.hasFocus = true;
16478             if(this.triggerAction == 'all') {
16479                 this.doQuery(this.allQuery, true);
16480             } else {
16481                 this.doQuery(this.getRawValue());
16482             }
16483             if (!this.blockFocus) {
16484                 this.inputEl().focus();
16485             }
16486         }
16487     },
16488     
16489     onTickableTriggerClick : function(e)
16490     {
16491         if(this.disabled){
16492             return;
16493         }
16494         
16495         this.page = 0;
16496         this.loadNext = false;
16497         this.hasFocus = true;
16498         
16499         if(this.triggerAction == 'all') {
16500             this.doQuery(this.allQuery, true);
16501         } else {
16502             this.doQuery(this.getRawValue());
16503         }
16504     },
16505     
16506     onSearchFieldClick : function(e)
16507     {
16508         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16509             this.onTickableFooterButtonClick(e, false, false);
16510             return;
16511         }
16512         
16513         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16514             return;
16515         }
16516         
16517         this.page = 0;
16518         this.loadNext = false;
16519         this.hasFocus = true;
16520         
16521         if(this.triggerAction == 'all') {
16522             this.doQuery(this.allQuery, true);
16523         } else {
16524             this.doQuery(this.getRawValue());
16525         }
16526     },
16527     
16528     listKeyPress : function(e)
16529     {
16530         //Roo.log('listkeypress');
16531         // scroll to first matching element based on key pres..
16532         if (e.isSpecialKey()) {
16533             return false;
16534         }
16535         var k = String.fromCharCode(e.getKey()).toUpperCase();
16536         //Roo.log(k);
16537         var match  = false;
16538         var csel = this.view.getSelectedNodes();
16539         var cselitem = false;
16540         if (csel.length) {
16541             var ix = this.view.indexOf(csel[0]);
16542             cselitem  = this.store.getAt(ix);
16543             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16544                 cselitem = false;
16545             }
16546             
16547         }
16548         
16549         this.store.each(function(v) { 
16550             if (cselitem) {
16551                 // start at existing selection.
16552                 if (cselitem.id == v.id) {
16553                     cselitem = false;
16554                 }
16555                 return true;
16556             }
16557                 
16558             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16559                 match = this.store.indexOf(v);
16560                 return false;
16561             }
16562             return true;
16563         }, this);
16564         
16565         if (match === false) {
16566             return true; // no more action?
16567         }
16568         // scroll to?
16569         this.view.select(match);
16570         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16571         sn.scrollIntoView(sn.dom.parentNode, false);
16572     },
16573     
16574     onViewScroll : function(e, t){
16575         
16576         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
16577             return;
16578         }
16579         
16580         this.hasQuery = true;
16581         
16582         this.loading = this.list.select('.loading', true).first();
16583         
16584         if(this.loading === null){
16585             this.list.createChild({
16586                 tag: 'div',
16587                 cls: 'loading roo-select2-more-results roo-select2-active',
16588                 html: 'Loading more results...'
16589             });
16590             
16591             this.loading = this.list.select('.loading', true).first();
16592             
16593             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16594             
16595             this.loading.hide();
16596         }
16597         
16598         this.loading.show();
16599         
16600         var _combo = this;
16601         
16602         this.page++;
16603         this.loadNext = true;
16604         
16605         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16606         
16607         return;
16608     },
16609     
16610     addItem : function(o)
16611     {   
16612         var dv = ''; // display value
16613         
16614         if (this.displayField) {
16615             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16616         } else {
16617             // this is an error condition!!!
16618             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16619         }
16620         
16621         if(!dv.length){
16622             return;
16623         }
16624         
16625         var choice = this.choices.createChild({
16626             tag: 'li',
16627             cls: 'roo-select2-search-choice',
16628             cn: [
16629                 {
16630                     tag: 'div',
16631                     html: dv
16632                 },
16633                 {
16634                     tag: 'a',
16635                     href: '#',
16636                     cls: 'roo-select2-search-choice-close fa fa-times',
16637                     tabindex: '-1'
16638                 }
16639             ]
16640             
16641         }, this.searchField);
16642         
16643         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16644         
16645         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16646         
16647         this.item.push(o);
16648         
16649         this.lastData = o;
16650         
16651         this.syncValue();
16652         
16653         this.inputEl().dom.value = '';
16654         
16655         this.validate();
16656     },
16657     
16658     onRemoveItem : function(e, _self, o)
16659     {
16660         e.preventDefault();
16661         
16662         this.lastItem = Roo.apply([], this.item);
16663         
16664         var index = this.item.indexOf(o.data) * 1;
16665         
16666         if( index < 0){
16667             Roo.log('not this item?!');
16668             return;
16669         }
16670         
16671         this.item.splice(index, 1);
16672         o.item.remove();
16673         
16674         this.syncValue();
16675         
16676         this.fireEvent('remove', this, e);
16677         
16678         this.validate();
16679         
16680     },
16681     
16682     syncValue : function()
16683     {
16684         if(!this.item.length){
16685             this.clearValue();
16686             return;
16687         }
16688             
16689         var value = [];
16690         var _this = this;
16691         Roo.each(this.item, function(i){
16692             if(_this.valueField){
16693                 value.push(i[_this.valueField]);
16694                 return;
16695             }
16696
16697             value.push(i);
16698         });
16699
16700         this.value = value.join(',');
16701
16702         if(this.hiddenField){
16703             this.hiddenField.dom.value = this.value;
16704         }
16705         
16706         this.store.fireEvent("datachanged", this.store);
16707         
16708         this.validate();
16709     },
16710     
16711     clearItem : function()
16712     {
16713         if(!this.multiple){
16714             return;
16715         }
16716         
16717         this.item = [];
16718         
16719         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16720            c.remove();
16721         });
16722         
16723         this.syncValue();
16724         
16725         this.validate();
16726         
16727         if(this.tickable && !Roo.isTouch){
16728             this.view.refresh();
16729         }
16730     },
16731     
16732     inputEl: function ()
16733     {
16734         if(Roo.isIOS && this.useNativeIOS){
16735             return this.el.select('select.roo-ios-select', true).first();
16736         }
16737         
16738         if(Roo.isTouch && this.mobileTouchView){
16739             return this.el.select('input.form-control',true).first();
16740         }
16741         
16742         if(this.tickable){
16743             return this.searchField;
16744         }
16745         
16746         return this.el.select('input.form-control',true).first();
16747     },
16748     
16749     onTickableFooterButtonClick : function(e, btn, el)
16750     {
16751         e.preventDefault();
16752         
16753         this.lastItem = Roo.apply([], this.item);
16754         
16755         if(btn && btn.name == 'cancel'){
16756             this.tickItems = Roo.apply([], this.item);
16757             this.collapse();
16758             return;
16759         }
16760         
16761         this.clearItem();
16762         
16763         var _this = this;
16764         
16765         Roo.each(this.tickItems, function(o){
16766             _this.addItem(o);
16767         });
16768         
16769         this.collapse();
16770         
16771     },
16772     
16773     validate : function()
16774     {
16775         if(this.getVisibilityEl().hasClass('hidden')){
16776             return true;
16777         }
16778         
16779         var v = this.getRawValue();
16780         
16781         if(this.multiple){
16782             v = this.getValue();
16783         }
16784         
16785         if(this.disabled || this.allowBlank || v.length){
16786             this.markValid();
16787             return true;
16788         }
16789         
16790         this.markInvalid();
16791         return false;
16792     },
16793     
16794     tickableInputEl : function()
16795     {
16796         if(!this.tickable || !this.editable){
16797             return this.inputEl();
16798         }
16799         
16800         return this.inputEl().select('.roo-select2-search-field-input', true).first();
16801     },
16802     
16803     
16804     getAutoCreateTouchView : function()
16805     {
16806         var id = Roo.id();
16807         
16808         var cfg = {
16809             cls: 'form-group' //input-group
16810         };
16811         
16812         var input =  {
16813             tag: 'input',
16814             id : id,
16815             type : this.inputType,
16816             cls : 'form-control x-combo-noedit',
16817             autocomplete: 'new-password',
16818             placeholder : this.placeholder || '',
16819             readonly : true
16820         };
16821         
16822         if (this.name) {
16823             input.name = this.name;
16824         }
16825         
16826         if (this.size) {
16827             input.cls += ' input-' + this.size;
16828         }
16829         
16830         if (this.disabled) {
16831             input.disabled = true;
16832         }
16833         
16834         var inputblock = {
16835             cls : '',
16836             cn : [
16837                 input
16838             ]
16839         };
16840         
16841         if(this.before){
16842             inputblock.cls += ' input-group';
16843             
16844             inputblock.cn.unshift({
16845                 tag :'span',
16846                 cls : 'input-group-addon input-group-prepend input-group-text',
16847                 html : this.before
16848             });
16849         }
16850         
16851         if(this.removable && !this.multiple){
16852             inputblock.cls += ' roo-removable';
16853             
16854             inputblock.cn.push({
16855                 tag: 'button',
16856                 html : 'x',
16857                 cls : 'roo-combo-removable-btn close'
16858             });
16859         }
16860
16861         if(this.hasFeedback && !this.allowBlank){
16862             
16863             inputblock.cls += ' has-feedback';
16864             
16865             inputblock.cn.push({
16866                 tag: 'span',
16867                 cls: 'glyphicon form-control-feedback'
16868             });
16869             
16870         }
16871         
16872         if (this.after) {
16873             
16874             inputblock.cls += (this.before) ? '' : ' input-group';
16875             
16876             inputblock.cn.push({
16877                 tag :'span',
16878                 cls : 'input-group-addon input-group-append input-group-text',
16879                 html : this.after
16880             });
16881         }
16882
16883         
16884         var ibwrap = inputblock;
16885         
16886         if(this.multiple){
16887             ibwrap = {
16888                 tag: 'ul',
16889                 cls: 'roo-select2-choices',
16890                 cn:[
16891                     {
16892                         tag: 'li',
16893                         cls: 'roo-select2-search-field',
16894                         cn: [
16895
16896                             inputblock
16897                         ]
16898                     }
16899                 ]
16900             };
16901         
16902             
16903         }
16904         
16905         var combobox = {
16906             cls: 'roo-select2-container input-group roo-touchview-combobox ',
16907             cn: [
16908                 {
16909                     tag: 'input',
16910                     type : 'hidden',
16911                     cls: 'form-hidden-field'
16912                 },
16913                 ibwrap
16914             ]
16915         };
16916         
16917         if(!this.multiple && this.showToggleBtn){
16918             
16919             var caret = {
16920                 cls: 'caret'
16921             };
16922             
16923             if (this.caret != false) {
16924                 caret = {
16925                      tag: 'i',
16926                      cls: 'fa fa-' + this.caret
16927                 };
16928                 
16929             }
16930             
16931             combobox.cn.push({
16932                 tag :'span',
16933                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
16934                 cn : [
16935                     Roo.bootstrap.version == 3 ? caret : '',
16936                     {
16937                         tag: 'span',
16938                         cls: 'combobox-clear',
16939                         cn  : [
16940                             {
16941                                 tag : 'i',
16942                                 cls: 'icon-remove'
16943                             }
16944                         ]
16945                     }
16946                 ]
16947
16948             })
16949         }
16950         
16951         if(this.multiple){
16952             combobox.cls += ' roo-select2-container-multi';
16953         }
16954         
16955         var align = this.labelAlign || this.parentLabelAlign();
16956         
16957         if (align ==='left' && this.fieldLabel.length) {
16958
16959             cfg.cn = [
16960                 {
16961                    tag : 'i',
16962                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
16963                    tooltip : 'This field is required'
16964                 },
16965                 {
16966                     tag: 'label',
16967                     cls : 'control-label col-form-label',
16968                     html : this.fieldLabel
16969
16970                 },
16971                 {
16972                     cls : '', 
16973                     cn: [
16974                         combobox
16975                     ]
16976                 }
16977             ];
16978             
16979             var labelCfg = cfg.cn[1];
16980             var contentCfg = cfg.cn[2];
16981             
16982
16983             if(this.indicatorpos == 'right'){
16984                 cfg.cn = [
16985                     {
16986                         tag: 'label',
16987                         'for' :  id,
16988                         cls : 'control-label col-form-label',
16989                         cn : [
16990                             {
16991                                 tag : 'span',
16992                                 html : this.fieldLabel
16993                             },
16994                             {
16995                                 tag : 'i',
16996                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
16997                                 tooltip : 'This field is required'
16998                             }
16999                         ]
17000                     },
17001                     {
17002                         cls : "",
17003                         cn: [
17004                             combobox
17005                         ]
17006                     }
17007
17008                 ];
17009                 
17010                 labelCfg = cfg.cn[0];
17011                 contentCfg = cfg.cn[1];
17012             }
17013             
17014            
17015             
17016             if(this.labelWidth > 12){
17017                 labelCfg.style = "width: " + this.labelWidth + 'px';
17018             }
17019             
17020             if(this.labelWidth < 13 && this.labelmd == 0){
17021                 this.labelmd = this.labelWidth;
17022             }
17023             
17024             if(this.labellg > 0){
17025                 labelCfg.cls += ' col-lg-' + this.labellg;
17026                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17027             }
17028             
17029             if(this.labelmd > 0){
17030                 labelCfg.cls += ' col-md-' + this.labelmd;
17031                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17032             }
17033             
17034             if(this.labelsm > 0){
17035                 labelCfg.cls += ' col-sm-' + this.labelsm;
17036                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17037             }
17038             
17039             if(this.labelxs > 0){
17040                 labelCfg.cls += ' col-xs-' + this.labelxs;
17041                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17042             }
17043                 
17044                 
17045         } else if ( this.fieldLabel.length) {
17046             cfg.cn = [
17047                 {
17048                    tag : 'i',
17049                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17050                    tooltip : 'This field is required'
17051                 },
17052                 {
17053                     tag: 'label',
17054                     cls : 'control-label',
17055                     html : this.fieldLabel
17056
17057                 },
17058                 {
17059                     cls : '', 
17060                     cn: [
17061                         combobox
17062                     ]
17063                 }
17064             ];
17065             
17066             if(this.indicatorpos == 'right'){
17067                 cfg.cn = [
17068                     {
17069                         tag: 'label',
17070                         cls : 'control-label',
17071                         html : this.fieldLabel,
17072                         cn : [
17073                             {
17074                                tag : 'i',
17075                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17076                                tooltip : 'This field is required'
17077                             }
17078                         ]
17079                     },
17080                     {
17081                         cls : '', 
17082                         cn: [
17083                             combobox
17084                         ]
17085                     }
17086                 ];
17087             }
17088         } else {
17089             cfg.cn = combobox;    
17090         }
17091         
17092         
17093         var settings = this;
17094         
17095         ['xs','sm','md','lg'].map(function(size){
17096             if (settings[size]) {
17097                 cfg.cls += ' col-' + size + '-' + settings[size];
17098             }
17099         });
17100         
17101         return cfg;
17102     },
17103     
17104     initTouchView : function()
17105     {
17106         this.renderTouchView();
17107         
17108         this.touchViewEl.on('scroll', function(){
17109             this.el.dom.scrollTop = 0;
17110         }, this);
17111         
17112         this.originalValue = this.getValue();
17113         
17114         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17115         
17116         this.inputEl().on("click", this.showTouchView, this);
17117         if (this.triggerEl) {
17118             this.triggerEl.on("click", this.showTouchView, this);
17119         }
17120         
17121         
17122         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17123         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17124         
17125         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17126         
17127         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17128         this.store.on('load', this.onTouchViewLoad, this);
17129         this.store.on('loadexception', this.onTouchViewLoadException, this);
17130         
17131         if(this.hiddenName){
17132             
17133             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17134             
17135             this.hiddenField.dom.value =
17136                 this.hiddenValue !== undefined ? this.hiddenValue :
17137                 this.value !== undefined ? this.value : '';
17138         
17139             this.el.dom.removeAttribute('name');
17140             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17141         }
17142         
17143         if(this.multiple){
17144             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17145             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17146         }
17147         
17148         if(this.removable && !this.multiple){
17149             var close = this.closeTriggerEl();
17150             if(close){
17151                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17152                 close.on('click', this.removeBtnClick, this, close);
17153             }
17154         }
17155         /*
17156          * fix the bug in Safari iOS8
17157          */
17158         this.inputEl().on("focus", function(e){
17159             document.activeElement.blur();
17160         }, this);
17161         
17162         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17163         
17164         return;
17165         
17166         
17167     },
17168     
17169     renderTouchView : function()
17170     {
17171         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17172         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17173         
17174         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17175         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17176         
17177         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17178         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17179         this.touchViewBodyEl.setStyle('overflow', 'auto');
17180         
17181         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17182         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17183         
17184         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17185         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17186         
17187     },
17188     
17189     showTouchView : function()
17190     {
17191         if(this.disabled){
17192             return;
17193         }
17194         
17195         this.touchViewHeaderEl.hide();
17196
17197         if(this.modalTitle.length){
17198             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17199             this.touchViewHeaderEl.show();
17200         }
17201
17202         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17203         this.touchViewEl.show();
17204
17205         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17206         
17207         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17208         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17209
17210         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17211
17212         if(this.modalTitle.length){
17213             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17214         }
17215         
17216         this.touchViewBodyEl.setHeight(bodyHeight);
17217
17218         if(this.animate){
17219             var _this = this;
17220             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
17221         }else{
17222             this.touchViewEl.addClass('in');
17223         }
17224         
17225         if(this._touchViewMask){
17226             Roo.get(document.body).addClass("x-body-masked");
17227             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17228             this._touchViewMask.setStyle('z-index', 10000);
17229             this._touchViewMask.addClass('show');
17230         }
17231         
17232         this.doTouchViewQuery();
17233         
17234     },
17235     
17236     hideTouchView : function()
17237     {
17238         this.touchViewEl.removeClass('in');
17239
17240         if(this.animate){
17241             var _this = this;
17242             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17243         }else{
17244             this.touchViewEl.setStyle('display', 'none');
17245         }
17246         
17247         if(this._touchViewMask){
17248             this._touchViewMask.removeClass('show');
17249             Roo.get(document.body).removeClass("x-body-masked");
17250         }
17251     },
17252     
17253     setTouchViewValue : function()
17254     {
17255         if(this.multiple){
17256             this.clearItem();
17257         
17258             var _this = this;
17259
17260             Roo.each(this.tickItems, function(o){
17261                 this.addItem(o);
17262             }, this);
17263         }
17264         
17265         this.hideTouchView();
17266     },
17267     
17268     doTouchViewQuery : function()
17269     {
17270         var qe = {
17271             query: '',
17272             forceAll: true,
17273             combo: this,
17274             cancel:false
17275         };
17276         
17277         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17278             return false;
17279         }
17280         
17281         if(!this.alwaysQuery || this.mode == 'local'){
17282             this.onTouchViewLoad();
17283             return;
17284         }
17285         
17286         this.store.load();
17287     },
17288     
17289     onTouchViewBeforeLoad : function(combo,opts)
17290     {
17291         return;
17292     },
17293
17294     // private
17295     onTouchViewLoad : function()
17296     {
17297         if(this.store.getCount() < 1){
17298             this.onTouchViewEmptyResults();
17299             return;
17300         }
17301         
17302         this.clearTouchView();
17303         
17304         var rawValue = this.getRawValue();
17305         
17306         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17307         
17308         this.tickItems = [];
17309         
17310         this.store.data.each(function(d, rowIndex){
17311             var row = this.touchViewListGroup.createChild(template);
17312             
17313             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17314                 row.addClass(d.data.cls);
17315             }
17316             
17317             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17318                 var cfg = {
17319                     data : d.data,
17320                     html : d.data[this.displayField]
17321                 };
17322                 
17323                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17324                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17325                 }
17326             }
17327             row.removeClass('selected');
17328             if(!this.multiple && this.valueField &&
17329                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17330             {
17331                 // radio buttons..
17332                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17333                 row.addClass('selected');
17334             }
17335             
17336             if(this.multiple && this.valueField &&
17337                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17338             {
17339                 
17340                 // checkboxes...
17341                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17342                 this.tickItems.push(d.data);
17343             }
17344             
17345             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17346             
17347         }, this);
17348         
17349         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17350         
17351         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17352
17353         if(this.modalTitle.length){
17354             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17355         }
17356
17357         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17358         
17359         if(this.mobile_restrict_height && listHeight < bodyHeight){
17360             this.touchViewBodyEl.setHeight(listHeight);
17361         }
17362         
17363         var _this = this;
17364         
17365         if(firstChecked && listHeight > bodyHeight){
17366             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17367         }
17368         
17369     },
17370     
17371     onTouchViewLoadException : function()
17372     {
17373         this.hideTouchView();
17374     },
17375     
17376     onTouchViewEmptyResults : function()
17377     {
17378         this.clearTouchView();
17379         
17380         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17381         
17382         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17383         
17384     },
17385     
17386     clearTouchView : function()
17387     {
17388         this.touchViewListGroup.dom.innerHTML = '';
17389     },
17390     
17391     onTouchViewClick : function(e, el, o)
17392     {
17393         e.preventDefault();
17394         
17395         var row = o.row;
17396         var rowIndex = o.rowIndex;
17397         
17398         var r = this.store.getAt(rowIndex);
17399         
17400         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17401             
17402             if(!this.multiple){
17403                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17404                     c.dom.removeAttribute('checked');
17405                 }, this);
17406
17407                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17408
17409                 this.setFromData(r.data);
17410
17411                 var close = this.closeTriggerEl();
17412
17413                 if(close){
17414                     close.show();
17415                 }
17416
17417                 this.hideTouchView();
17418
17419                 this.fireEvent('select', this, r, rowIndex);
17420
17421                 return;
17422             }
17423
17424             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17425                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17426                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17427                 return;
17428             }
17429
17430             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17431             this.addItem(r.data);
17432             this.tickItems.push(r.data);
17433         }
17434     },
17435     
17436     getAutoCreateNativeIOS : function()
17437     {
17438         var cfg = {
17439             cls: 'form-group' //input-group,
17440         };
17441         
17442         var combobox =  {
17443             tag: 'select',
17444             cls : 'roo-ios-select'
17445         };
17446         
17447         if (this.name) {
17448             combobox.name = this.name;
17449         }
17450         
17451         if (this.disabled) {
17452             combobox.disabled = true;
17453         }
17454         
17455         var settings = this;
17456         
17457         ['xs','sm','md','lg'].map(function(size){
17458             if (settings[size]) {
17459                 cfg.cls += ' col-' + size + '-' + settings[size];
17460             }
17461         });
17462         
17463         cfg.cn = combobox;
17464         
17465         return cfg;
17466         
17467     },
17468     
17469     initIOSView : function()
17470     {
17471         this.store.on('load', this.onIOSViewLoad, this);
17472         
17473         return;
17474     },
17475     
17476     onIOSViewLoad : function()
17477     {
17478         if(this.store.getCount() < 1){
17479             return;
17480         }
17481         
17482         this.clearIOSView();
17483         
17484         if(this.allowBlank) {
17485             
17486             var default_text = '-- SELECT --';
17487             
17488             if(this.placeholder.length){
17489                 default_text = this.placeholder;
17490             }
17491             
17492             if(this.emptyTitle.length){
17493                 default_text += ' - ' + this.emptyTitle + ' -';
17494             }
17495             
17496             var opt = this.inputEl().createChild({
17497                 tag: 'option',
17498                 value : 0,
17499                 html : default_text
17500             });
17501             
17502             var o = {};
17503             o[this.valueField] = 0;
17504             o[this.displayField] = default_text;
17505             
17506             this.ios_options.push({
17507                 data : o,
17508                 el : opt
17509             });
17510             
17511         }
17512         
17513         this.store.data.each(function(d, rowIndex){
17514             
17515             var html = '';
17516             
17517             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17518                 html = d.data[this.displayField];
17519             }
17520             
17521             var value = '';
17522             
17523             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17524                 value = d.data[this.valueField];
17525             }
17526             
17527             var option = {
17528                 tag: 'option',
17529                 value : value,
17530                 html : html
17531             };
17532             
17533             if(this.value == d.data[this.valueField]){
17534                 option['selected'] = true;
17535             }
17536             
17537             var opt = this.inputEl().createChild(option);
17538             
17539             this.ios_options.push({
17540                 data : d.data,
17541                 el : opt
17542             });
17543             
17544         }, this);
17545         
17546         this.inputEl().on('change', function(){
17547            this.fireEvent('select', this);
17548         }, this);
17549         
17550     },
17551     
17552     clearIOSView: function()
17553     {
17554         this.inputEl().dom.innerHTML = '';
17555         
17556         this.ios_options = [];
17557     },
17558     
17559     setIOSValue: function(v)
17560     {
17561         this.value = v;
17562         
17563         if(!this.ios_options){
17564             return;
17565         }
17566         
17567         Roo.each(this.ios_options, function(opts){
17568            
17569            opts.el.dom.removeAttribute('selected');
17570            
17571            if(opts.data[this.valueField] != v){
17572                return;
17573            }
17574            
17575            opts.el.dom.setAttribute('selected', true);
17576            
17577         }, this);
17578     }
17579
17580     /** 
17581     * @cfg {Boolean} grow 
17582     * @hide 
17583     */
17584     /** 
17585     * @cfg {Number} growMin 
17586     * @hide 
17587     */
17588     /** 
17589     * @cfg {Number} growMax 
17590     * @hide 
17591     */
17592     /**
17593      * @hide
17594      * @method autoSize
17595      */
17596 });
17597
17598 Roo.apply(Roo.bootstrap.ComboBox,  {
17599     
17600     header : {
17601         tag: 'div',
17602         cls: 'modal-header',
17603         cn: [
17604             {
17605                 tag: 'h4',
17606                 cls: 'modal-title'
17607             }
17608         ]
17609     },
17610     
17611     body : {
17612         tag: 'div',
17613         cls: 'modal-body',
17614         cn: [
17615             {
17616                 tag: 'ul',
17617                 cls: 'list-group'
17618             }
17619         ]
17620     },
17621     
17622     listItemRadio : {
17623         tag: 'li',
17624         cls: 'list-group-item',
17625         cn: [
17626             {
17627                 tag: 'span',
17628                 cls: 'roo-combobox-list-group-item-value'
17629             },
17630             {
17631                 tag: 'div',
17632                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17633                 cn: [
17634                     {
17635                         tag: 'input',
17636                         type: 'radio'
17637                     },
17638                     {
17639                         tag: 'label'
17640                     }
17641                 ]
17642             }
17643         ]
17644     },
17645     
17646     listItemCheckbox : {
17647         tag: 'li',
17648         cls: 'list-group-item',
17649         cn: [
17650             {
17651                 tag: 'span',
17652                 cls: 'roo-combobox-list-group-item-value'
17653             },
17654             {
17655                 tag: 'div',
17656                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17657                 cn: [
17658                     {
17659                         tag: 'input',
17660                         type: 'checkbox'
17661                     },
17662                     {
17663                         tag: 'label'
17664                     }
17665                 ]
17666             }
17667         ]
17668     },
17669     
17670     emptyResult : {
17671         tag: 'div',
17672         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17673     },
17674     
17675     footer : {
17676         tag: 'div',
17677         cls: 'modal-footer',
17678         cn: [
17679             {
17680                 tag: 'div',
17681                 cls: 'row',
17682                 cn: [
17683                     {
17684                         tag: 'div',
17685                         cls: 'col-xs-6 text-left',
17686                         cn: {
17687                             tag: 'button',
17688                             cls: 'btn btn-danger roo-touch-view-cancel',
17689                             html: 'Cancel'
17690                         }
17691                     },
17692                     {
17693                         tag: 'div',
17694                         cls: 'col-xs-6 text-right',
17695                         cn: {
17696                             tag: 'button',
17697                             cls: 'btn btn-success roo-touch-view-ok',
17698                             html: 'OK'
17699                         }
17700                     }
17701                 ]
17702             }
17703         ]
17704         
17705     }
17706 });
17707
17708 Roo.apply(Roo.bootstrap.ComboBox,  {
17709     
17710     touchViewTemplate : {
17711         tag: 'div',
17712         cls: 'modal fade roo-combobox-touch-view',
17713         cn: [
17714             {
17715                 tag: 'div',
17716                 cls: 'modal-dialog',
17717                 style : 'position:fixed', // we have to fix position....
17718                 cn: [
17719                     {
17720                         tag: 'div',
17721                         cls: 'modal-content',
17722                         cn: [
17723                             Roo.bootstrap.ComboBox.header,
17724                             Roo.bootstrap.ComboBox.body,
17725                             Roo.bootstrap.ComboBox.footer
17726                         ]
17727                     }
17728                 ]
17729             }
17730         ]
17731     }
17732 });/*
17733  * Based on:
17734  * Ext JS Library 1.1.1
17735  * Copyright(c) 2006-2007, Ext JS, LLC.
17736  *
17737  * Originally Released Under LGPL - original licence link has changed is not relivant.
17738  *
17739  * Fork - LGPL
17740  * <script type="text/javascript">
17741  */
17742
17743 /**
17744  * @class Roo.View
17745  * @extends Roo.util.Observable
17746  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
17747  * This class also supports single and multi selection modes. <br>
17748  * Create a data model bound view:
17749  <pre><code>
17750  var store = new Roo.data.Store(...);
17751
17752  var view = new Roo.View({
17753     el : "my-element",
17754     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
17755  
17756     singleSelect: true,
17757     selectedClass: "ydataview-selected",
17758     store: store
17759  });
17760
17761  // listen for node click?
17762  view.on("click", function(vw, index, node, e){
17763  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17764  });
17765
17766  // load XML data
17767  dataModel.load("foobar.xml");
17768  </code></pre>
17769  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17770  * <br><br>
17771  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17772  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17773  * 
17774  * Note: old style constructor is still suported (container, template, config)
17775  * 
17776  * @constructor
17777  * Create a new View
17778  * @param {Object} config The config object
17779  * 
17780  */
17781 Roo.View = function(config, depreciated_tpl, depreciated_config){
17782     
17783     this.parent = false;
17784     
17785     if (typeof(depreciated_tpl) == 'undefined') {
17786         // new way.. - universal constructor.
17787         Roo.apply(this, config);
17788         this.el  = Roo.get(this.el);
17789     } else {
17790         // old format..
17791         this.el  = Roo.get(config);
17792         this.tpl = depreciated_tpl;
17793         Roo.apply(this, depreciated_config);
17794     }
17795     this.wrapEl  = this.el.wrap().wrap();
17796     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17797     
17798     
17799     if(typeof(this.tpl) == "string"){
17800         this.tpl = new Roo.Template(this.tpl);
17801     } else {
17802         // support xtype ctors..
17803         this.tpl = new Roo.factory(this.tpl, Roo);
17804     }
17805     
17806     
17807     this.tpl.compile();
17808     
17809     /** @private */
17810     this.addEvents({
17811         /**
17812          * @event beforeclick
17813          * Fires before a click is processed. Returns false to cancel the default action.
17814          * @param {Roo.View} this
17815          * @param {Number} index The index of the target node
17816          * @param {HTMLElement} node The target node
17817          * @param {Roo.EventObject} e The raw event object
17818          */
17819             "beforeclick" : true,
17820         /**
17821          * @event click
17822          * Fires when a template node is clicked.
17823          * @param {Roo.View} this
17824          * @param {Number} index The index of the target node
17825          * @param {HTMLElement} node The target node
17826          * @param {Roo.EventObject} e The raw event object
17827          */
17828             "click" : true,
17829         /**
17830          * @event dblclick
17831          * Fires when a template node is double clicked.
17832          * @param {Roo.View} this
17833          * @param {Number} index The index of the target node
17834          * @param {HTMLElement} node The target node
17835          * @param {Roo.EventObject} e The raw event object
17836          */
17837             "dblclick" : true,
17838         /**
17839          * @event contextmenu
17840          * Fires when a template node is right clicked.
17841          * @param {Roo.View} this
17842          * @param {Number} index The index of the target node
17843          * @param {HTMLElement} node The target node
17844          * @param {Roo.EventObject} e The raw event object
17845          */
17846             "contextmenu" : true,
17847         /**
17848          * @event selectionchange
17849          * Fires when the selected nodes change.
17850          * @param {Roo.View} this
17851          * @param {Array} selections Array of the selected nodes
17852          */
17853             "selectionchange" : true,
17854     
17855         /**
17856          * @event beforeselect
17857          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17858          * @param {Roo.View} this
17859          * @param {HTMLElement} node The node to be selected
17860          * @param {Array} selections Array of currently selected nodes
17861          */
17862             "beforeselect" : true,
17863         /**
17864          * @event preparedata
17865          * Fires on every row to render, to allow you to change the data.
17866          * @param {Roo.View} this
17867          * @param {Object} data to be rendered (change this)
17868          */
17869           "preparedata" : true
17870           
17871           
17872         });
17873
17874
17875
17876     this.el.on({
17877         "click": this.onClick,
17878         "dblclick": this.onDblClick,
17879         "contextmenu": this.onContextMenu,
17880         scope:this
17881     });
17882
17883     this.selections = [];
17884     this.nodes = [];
17885     this.cmp = new Roo.CompositeElementLite([]);
17886     if(this.store){
17887         this.store = Roo.factory(this.store, Roo.data);
17888         this.setStore(this.store, true);
17889     }
17890     
17891     if ( this.footer && this.footer.xtype) {
17892            
17893          var fctr = this.wrapEl.appendChild(document.createElement("div"));
17894         
17895         this.footer.dataSource = this.store;
17896         this.footer.container = fctr;
17897         this.footer = Roo.factory(this.footer, Roo);
17898         fctr.insertFirst(this.el);
17899         
17900         // this is a bit insane - as the paging toolbar seems to detach the el..
17901 //        dom.parentNode.parentNode.parentNode
17902          // they get detached?
17903     }
17904     
17905     
17906     Roo.View.superclass.constructor.call(this);
17907     
17908     
17909 };
17910
17911 Roo.extend(Roo.View, Roo.util.Observable, {
17912     
17913      /**
17914      * @cfg {Roo.data.Store} store Data store to load data from.
17915      */
17916     store : false,
17917     
17918     /**
17919      * @cfg {String|Roo.Element} el The container element.
17920      */
17921     el : '',
17922     
17923     /**
17924      * @cfg {String|Roo.Template} tpl The template used by this View 
17925      */
17926     tpl : false,
17927     /**
17928      * @cfg {String} dataName the named area of the template to use as the data area
17929      *                          Works with domtemplates roo-name="name"
17930      */
17931     dataName: false,
17932     /**
17933      * @cfg {String} selectedClass The css class to add to selected nodes
17934      */
17935     selectedClass : "x-view-selected",
17936      /**
17937      * @cfg {String} emptyText The empty text to show when nothing is loaded.
17938      */
17939     emptyText : "",
17940     
17941     /**
17942      * @cfg {String} text to display on mask (default Loading)
17943      */
17944     mask : false,
17945     /**
17946      * @cfg {Boolean} multiSelect Allow multiple selection
17947      */
17948     multiSelect : false,
17949     /**
17950      * @cfg {Boolean} singleSelect Allow single selection
17951      */
17952     singleSelect:  false,
17953     
17954     /**
17955      * @cfg {Boolean} toggleSelect - selecting 
17956      */
17957     toggleSelect : false,
17958     
17959     /**
17960      * @cfg {Boolean} tickable - selecting 
17961      */
17962     tickable : false,
17963     
17964     /**
17965      * Returns the element this view is bound to.
17966      * @return {Roo.Element}
17967      */
17968     getEl : function(){
17969         return this.wrapEl;
17970     },
17971     
17972     
17973
17974     /**
17975      * Refreshes the view. - called by datachanged on the store. - do not call directly.
17976      */
17977     refresh : function(){
17978         //Roo.log('refresh');
17979         var t = this.tpl;
17980         
17981         // if we are using something like 'domtemplate', then
17982         // the what gets used is:
17983         // t.applySubtemplate(NAME, data, wrapping data..)
17984         // the outer template then get' applied with
17985         //     the store 'extra data'
17986         // and the body get's added to the
17987         //      roo-name="data" node?
17988         //      <span class='roo-tpl-{name}'></span> ?????
17989         
17990         
17991         
17992         this.clearSelections();
17993         this.el.update("");
17994         var html = [];
17995         var records = this.store.getRange();
17996         if(records.length < 1) {
17997             
17998             // is this valid??  = should it render a template??
17999             
18000             this.el.update(this.emptyText);
18001             return;
18002         }
18003         var el = this.el;
18004         if (this.dataName) {
18005             this.el.update(t.apply(this.store.meta)); //????
18006             el = this.el.child('.roo-tpl-' + this.dataName);
18007         }
18008         
18009         for(var i = 0, len = records.length; i < len; i++){
18010             var data = this.prepareData(records[i].data, i, records[i]);
18011             this.fireEvent("preparedata", this, data, i, records[i]);
18012             
18013             var d = Roo.apply({}, data);
18014             
18015             if(this.tickable){
18016                 Roo.apply(d, {'roo-id' : Roo.id()});
18017                 
18018                 var _this = this;
18019             
18020                 Roo.each(this.parent.item, function(item){
18021                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18022                         return;
18023                     }
18024                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18025                 });
18026             }
18027             
18028             html[html.length] = Roo.util.Format.trim(
18029                 this.dataName ?
18030                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18031                     t.apply(d)
18032             );
18033         }
18034         
18035         
18036         
18037         el.update(html.join(""));
18038         this.nodes = el.dom.childNodes;
18039         this.updateIndexes(0);
18040     },
18041     
18042
18043     /**
18044      * Function to override to reformat the data that is sent to
18045      * the template for each node.
18046      * DEPRICATED - use the preparedata event handler.
18047      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18048      * a JSON object for an UpdateManager bound view).
18049      */
18050     prepareData : function(data, index, record)
18051     {
18052         this.fireEvent("preparedata", this, data, index, record);
18053         return data;
18054     },
18055
18056     onUpdate : function(ds, record){
18057         // Roo.log('on update');   
18058         this.clearSelections();
18059         var index = this.store.indexOf(record);
18060         var n = this.nodes[index];
18061         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18062         n.parentNode.removeChild(n);
18063         this.updateIndexes(index, index);
18064     },
18065
18066     
18067     
18068 // --------- FIXME     
18069     onAdd : function(ds, records, index)
18070     {
18071         //Roo.log(['on Add', ds, records, index] );        
18072         this.clearSelections();
18073         if(this.nodes.length == 0){
18074             this.refresh();
18075             return;
18076         }
18077         var n = this.nodes[index];
18078         for(var i = 0, len = records.length; i < len; i++){
18079             var d = this.prepareData(records[i].data, i, records[i]);
18080             if(n){
18081                 this.tpl.insertBefore(n, d);
18082             }else{
18083                 
18084                 this.tpl.append(this.el, d);
18085             }
18086         }
18087         this.updateIndexes(index);
18088     },
18089
18090     onRemove : function(ds, record, index){
18091        // Roo.log('onRemove');
18092         this.clearSelections();
18093         var el = this.dataName  ?
18094             this.el.child('.roo-tpl-' + this.dataName) :
18095             this.el; 
18096         
18097         el.dom.removeChild(this.nodes[index]);
18098         this.updateIndexes(index);
18099     },
18100
18101     /**
18102      * Refresh an individual node.
18103      * @param {Number} index
18104      */
18105     refreshNode : function(index){
18106         this.onUpdate(this.store, this.store.getAt(index));
18107     },
18108
18109     updateIndexes : function(startIndex, endIndex){
18110         var ns = this.nodes;
18111         startIndex = startIndex || 0;
18112         endIndex = endIndex || ns.length - 1;
18113         for(var i = startIndex; i <= endIndex; i++){
18114             ns[i].nodeIndex = i;
18115         }
18116     },
18117
18118     /**
18119      * Changes the data store this view uses and refresh the view.
18120      * @param {Store} store
18121      */
18122     setStore : function(store, initial){
18123         if(!initial && this.store){
18124             this.store.un("datachanged", this.refresh);
18125             this.store.un("add", this.onAdd);
18126             this.store.un("remove", this.onRemove);
18127             this.store.un("update", this.onUpdate);
18128             this.store.un("clear", this.refresh);
18129             this.store.un("beforeload", this.onBeforeLoad);
18130             this.store.un("load", this.onLoad);
18131             this.store.un("loadexception", this.onLoad);
18132         }
18133         if(store){
18134           
18135             store.on("datachanged", this.refresh, this);
18136             store.on("add", this.onAdd, this);
18137             store.on("remove", this.onRemove, this);
18138             store.on("update", this.onUpdate, this);
18139             store.on("clear", this.refresh, this);
18140             store.on("beforeload", this.onBeforeLoad, this);
18141             store.on("load", this.onLoad, this);
18142             store.on("loadexception", this.onLoad, this);
18143         }
18144         
18145         if(store){
18146             this.refresh();
18147         }
18148     },
18149     /**
18150      * onbeforeLoad - masks the loading area.
18151      *
18152      */
18153     onBeforeLoad : function(store,opts)
18154     {
18155          //Roo.log('onBeforeLoad');   
18156         if (!opts.add) {
18157             this.el.update("");
18158         }
18159         this.el.mask(this.mask ? this.mask : "Loading" ); 
18160     },
18161     onLoad : function ()
18162     {
18163         this.el.unmask();
18164     },
18165     
18166
18167     /**
18168      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18169      * @param {HTMLElement} node
18170      * @return {HTMLElement} The template node
18171      */
18172     findItemFromChild : function(node){
18173         var el = this.dataName  ?
18174             this.el.child('.roo-tpl-' + this.dataName,true) :
18175             this.el.dom; 
18176         
18177         if(!node || node.parentNode == el){
18178                     return node;
18179             }
18180             var p = node.parentNode;
18181             while(p && p != el){
18182             if(p.parentNode == el){
18183                 return p;
18184             }
18185             p = p.parentNode;
18186         }
18187             return null;
18188     },
18189
18190     /** @ignore */
18191     onClick : function(e){
18192         var item = this.findItemFromChild(e.getTarget());
18193         if(item){
18194             var index = this.indexOf(item);
18195             if(this.onItemClick(item, index, e) !== false){
18196                 this.fireEvent("click", this, index, item, e);
18197             }
18198         }else{
18199             this.clearSelections();
18200         }
18201     },
18202
18203     /** @ignore */
18204     onContextMenu : function(e){
18205         var item = this.findItemFromChild(e.getTarget());
18206         if(item){
18207             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18208         }
18209     },
18210
18211     /** @ignore */
18212     onDblClick : function(e){
18213         var item = this.findItemFromChild(e.getTarget());
18214         if(item){
18215             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18216         }
18217     },
18218
18219     onItemClick : function(item, index, e)
18220     {
18221         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18222             return false;
18223         }
18224         if (this.toggleSelect) {
18225             var m = this.isSelected(item) ? 'unselect' : 'select';
18226             //Roo.log(m);
18227             var _t = this;
18228             _t[m](item, true, false);
18229             return true;
18230         }
18231         if(this.multiSelect || this.singleSelect){
18232             if(this.multiSelect && e.shiftKey && this.lastSelection){
18233                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18234             }else{
18235                 this.select(item, this.multiSelect && e.ctrlKey);
18236                 this.lastSelection = item;
18237             }
18238             
18239             if(!this.tickable){
18240                 e.preventDefault();
18241             }
18242             
18243         }
18244         return true;
18245     },
18246
18247     /**
18248      * Get the number of selected nodes.
18249      * @return {Number}
18250      */
18251     getSelectionCount : function(){
18252         return this.selections.length;
18253     },
18254
18255     /**
18256      * Get the currently selected nodes.
18257      * @return {Array} An array of HTMLElements
18258      */
18259     getSelectedNodes : function(){
18260         return this.selections;
18261     },
18262
18263     /**
18264      * Get the indexes of the selected nodes.
18265      * @return {Array}
18266      */
18267     getSelectedIndexes : function(){
18268         var indexes = [], s = this.selections;
18269         for(var i = 0, len = s.length; i < len; i++){
18270             indexes.push(s[i].nodeIndex);
18271         }
18272         return indexes;
18273     },
18274
18275     /**
18276      * Clear all selections
18277      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18278      */
18279     clearSelections : function(suppressEvent){
18280         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18281             this.cmp.elements = this.selections;
18282             this.cmp.removeClass(this.selectedClass);
18283             this.selections = [];
18284             if(!suppressEvent){
18285                 this.fireEvent("selectionchange", this, this.selections);
18286             }
18287         }
18288     },
18289
18290     /**
18291      * Returns true if the passed node is selected
18292      * @param {HTMLElement/Number} node The node or node index
18293      * @return {Boolean}
18294      */
18295     isSelected : function(node){
18296         var s = this.selections;
18297         if(s.length < 1){
18298             return false;
18299         }
18300         node = this.getNode(node);
18301         return s.indexOf(node) !== -1;
18302     },
18303
18304     /**
18305      * Selects nodes.
18306      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
18307      * @param {Boolean} keepExisting (optional) true to keep existing selections
18308      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18309      */
18310     select : function(nodeInfo, keepExisting, suppressEvent){
18311         if(nodeInfo instanceof Array){
18312             if(!keepExisting){
18313                 this.clearSelections(true);
18314             }
18315             for(var i = 0, len = nodeInfo.length; i < len; i++){
18316                 this.select(nodeInfo[i], true, true);
18317             }
18318             return;
18319         } 
18320         var node = this.getNode(nodeInfo);
18321         if(!node || this.isSelected(node)){
18322             return; // already selected.
18323         }
18324         if(!keepExisting){
18325             this.clearSelections(true);
18326         }
18327         
18328         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18329             Roo.fly(node).addClass(this.selectedClass);
18330             this.selections.push(node);
18331             if(!suppressEvent){
18332                 this.fireEvent("selectionchange", this, this.selections);
18333             }
18334         }
18335         
18336         
18337     },
18338       /**
18339      * Unselects nodes.
18340      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
18341      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18342      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18343      */
18344     unselect : function(nodeInfo, keepExisting, suppressEvent)
18345     {
18346         if(nodeInfo instanceof Array){
18347             Roo.each(this.selections, function(s) {
18348                 this.unselect(s, nodeInfo);
18349             }, this);
18350             return;
18351         }
18352         var node = this.getNode(nodeInfo);
18353         if(!node || !this.isSelected(node)){
18354             //Roo.log("not selected");
18355             return; // not selected.
18356         }
18357         // fireevent???
18358         var ns = [];
18359         Roo.each(this.selections, function(s) {
18360             if (s == node ) {
18361                 Roo.fly(node).removeClass(this.selectedClass);
18362
18363                 return;
18364             }
18365             ns.push(s);
18366         },this);
18367         
18368         this.selections= ns;
18369         this.fireEvent("selectionchange", this, this.selections);
18370     },
18371
18372     /**
18373      * Gets a template node.
18374      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18375      * @return {HTMLElement} The node or null if it wasn't found
18376      */
18377     getNode : function(nodeInfo){
18378         if(typeof nodeInfo == "string"){
18379             return document.getElementById(nodeInfo);
18380         }else if(typeof nodeInfo == "number"){
18381             return this.nodes[nodeInfo];
18382         }
18383         return nodeInfo;
18384     },
18385
18386     /**
18387      * Gets a range template nodes.
18388      * @param {Number} startIndex
18389      * @param {Number} endIndex
18390      * @return {Array} An array of nodes
18391      */
18392     getNodes : function(start, end){
18393         var ns = this.nodes;
18394         start = start || 0;
18395         end = typeof end == "undefined" ? ns.length - 1 : end;
18396         var nodes = [];
18397         if(start <= end){
18398             for(var i = start; i <= end; i++){
18399                 nodes.push(ns[i]);
18400             }
18401         } else{
18402             for(var i = start; i >= end; i--){
18403                 nodes.push(ns[i]);
18404             }
18405         }
18406         return nodes;
18407     },
18408
18409     /**
18410      * Finds the index of the passed node
18411      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18412      * @return {Number} The index of the node or -1
18413      */
18414     indexOf : function(node){
18415         node = this.getNode(node);
18416         if(typeof node.nodeIndex == "number"){
18417             return node.nodeIndex;
18418         }
18419         var ns = this.nodes;
18420         for(var i = 0, len = ns.length; i < len; i++){
18421             if(ns[i] == node){
18422                 return i;
18423             }
18424         }
18425         return -1;
18426     }
18427 });
18428 /*
18429  * - LGPL
18430  *
18431  * based on jquery fullcalendar
18432  * 
18433  */
18434
18435 Roo.bootstrap = Roo.bootstrap || {};
18436 /**
18437  * @class Roo.bootstrap.Calendar
18438  * @extends Roo.bootstrap.Component
18439  * Bootstrap Calendar class
18440  * @cfg {Boolean} loadMask (true|false) default false
18441  * @cfg {Object} header generate the user specific header of the calendar, default false
18442
18443  * @constructor
18444  * Create a new Container
18445  * @param {Object} config The config object
18446  */
18447
18448
18449
18450 Roo.bootstrap.Calendar = function(config){
18451     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18452      this.addEvents({
18453         /**
18454              * @event select
18455              * Fires when a date is selected
18456              * @param {DatePicker} this
18457              * @param {Date} date The selected date
18458              */
18459         'select': true,
18460         /**
18461              * @event monthchange
18462              * Fires when the displayed month changes 
18463              * @param {DatePicker} this
18464              * @param {Date} date The selected month
18465              */
18466         'monthchange': true,
18467         /**
18468              * @event evententer
18469              * Fires when mouse over an event
18470              * @param {Calendar} this
18471              * @param {event} Event
18472              */
18473         'evententer': true,
18474         /**
18475              * @event eventleave
18476              * Fires when the mouse leaves an
18477              * @param {Calendar} this
18478              * @param {event}
18479              */
18480         'eventleave': true,
18481         /**
18482              * @event eventclick
18483              * Fires when the mouse click an
18484              * @param {Calendar} this
18485              * @param {event}
18486              */
18487         'eventclick': true
18488         
18489     });
18490
18491 };
18492
18493 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18494     
18495      /**
18496      * @cfg {Number} startDay
18497      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18498      */
18499     startDay : 0,
18500     
18501     loadMask : false,
18502     
18503     header : false,
18504       
18505     getAutoCreate : function(){
18506         
18507         
18508         var fc_button = function(name, corner, style, content ) {
18509             return Roo.apply({},{
18510                 tag : 'span',
18511                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18512                          (corner.length ?
18513                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18514                             ''
18515                         ),
18516                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18517                 unselectable: 'on'
18518             });
18519         };
18520         
18521         var header = {};
18522         
18523         if(!this.header){
18524             header = {
18525                 tag : 'table',
18526                 cls : 'fc-header',
18527                 style : 'width:100%',
18528                 cn : [
18529                     {
18530                         tag: 'tr',
18531                         cn : [
18532                             {
18533                                 tag : 'td',
18534                                 cls : 'fc-header-left',
18535                                 cn : [
18536                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18537                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18538                                     { tag: 'span', cls: 'fc-header-space' },
18539                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18540
18541
18542                                 ]
18543                             },
18544
18545                             {
18546                                 tag : 'td',
18547                                 cls : 'fc-header-center',
18548                                 cn : [
18549                                     {
18550                                         tag: 'span',
18551                                         cls: 'fc-header-title',
18552                                         cn : {
18553                                             tag: 'H2',
18554                                             html : 'month / year'
18555                                         }
18556                                     }
18557
18558                                 ]
18559                             },
18560                             {
18561                                 tag : 'td',
18562                                 cls : 'fc-header-right',
18563                                 cn : [
18564                               /*      fc_button('month', 'left', '', 'month' ),
18565                                     fc_button('week', '', '', 'week' ),
18566                                     fc_button('day', 'right', '', 'day' )
18567                                 */    
18568
18569                                 ]
18570                             }
18571
18572                         ]
18573                     }
18574                 ]
18575             };
18576         }
18577         
18578         header = this.header;
18579         
18580        
18581         var cal_heads = function() {
18582             var ret = [];
18583             // fixme - handle this.
18584             
18585             for (var i =0; i < Date.dayNames.length; i++) {
18586                 var d = Date.dayNames[i];
18587                 ret.push({
18588                     tag: 'th',
18589                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18590                     html : d.substring(0,3)
18591                 });
18592                 
18593             }
18594             ret[0].cls += ' fc-first';
18595             ret[6].cls += ' fc-last';
18596             return ret;
18597         };
18598         var cal_cell = function(n) {
18599             return  {
18600                 tag: 'td',
18601                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18602                 cn : [
18603                     {
18604                         cn : [
18605                             {
18606                                 cls: 'fc-day-number',
18607                                 html: 'D'
18608                             },
18609                             {
18610                                 cls: 'fc-day-content',
18611                              
18612                                 cn : [
18613                                      {
18614                                         style: 'position: relative;' // height: 17px;
18615                                     }
18616                                 ]
18617                             }
18618                             
18619                             
18620                         ]
18621                     }
18622                 ]
18623                 
18624             }
18625         };
18626         var cal_rows = function() {
18627             
18628             var ret = [];
18629             for (var r = 0; r < 6; r++) {
18630                 var row= {
18631                     tag : 'tr',
18632                     cls : 'fc-week',
18633                     cn : []
18634                 };
18635                 
18636                 for (var i =0; i < Date.dayNames.length; i++) {
18637                     var d = Date.dayNames[i];
18638                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18639
18640                 }
18641                 row.cn[0].cls+=' fc-first';
18642                 row.cn[0].cn[0].style = 'min-height:90px';
18643                 row.cn[6].cls+=' fc-last';
18644                 ret.push(row);
18645                 
18646             }
18647             ret[0].cls += ' fc-first';
18648             ret[4].cls += ' fc-prev-last';
18649             ret[5].cls += ' fc-last';
18650             return ret;
18651             
18652         };
18653         
18654         var cal_table = {
18655             tag: 'table',
18656             cls: 'fc-border-separate',
18657             style : 'width:100%',
18658             cellspacing  : 0,
18659             cn : [
18660                 { 
18661                     tag: 'thead',
18662                     cn : [
18663                         { 
18664                             tag: 'tr',
18665                             cls : 'fc-first fc-last',
18666                             cn : cal_heads()
18667                         }
18668                     ]
18669                 },
18670                 { 
18671                     tag: 'tbody',
18672                     cn : cal_rows()
18673                 }
18674                   
18675             ]
18676         };
18677          
18678          var cfg = {
18679             cls : 'fc fc-ltr',
18680             cn : [
18681                 header,
18682                 {
18683                     cls : 'fc-content',
18684                     style : "position: relative;",
18685                     cn : [
18686                         {
18687                             cls : 'fc-view fc-view-month fc-grid',
18688                             style : 'position: relative',
18689                             unselectable : 'on',
18690                             cn : [
18691                                 {
18692                                     cls : 'fc-event-container',
18693                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18694                                 },
18695                                 cal_table
18696                             ]
18697                         }
18698                     ]
18699     
18700                 }
18701            ] 
18702             
18703         };
18704         
18705          
18706         
18707         return cfg;
18708     },
18709     
18710     
18711     initEvents : function()
18712     {
18713         if(!this.store){
18714             throw "can not find store for calendar";
18715         }
18716         
18717         var mark = {
18718             tag: "div",
18719             cls:"x-dlg-mask",
18720             style: "text-align:center",
18721             cn: [
18722                 {
18723                     tag: "div",
18724                     style: "background-color:white;width:50%;margin:250 auto",
18725                     cn: [
18726                         {
18727                             tag: "img",
18728                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18729                         },
18730                         {
18731                             tag: "span",
18732                             html: "Loading"
18733                         }
18734                         
18735                     ]
18736                 }
18737             ]
18738         };
18739         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18740         
18741         var size = this.el.select('.fc-content', true).first().getSize();
18742         this.maskEl.setSize(size.width, size.height);
18743         this.maskEl.enableDisplayMode("block");
18744         if(!this.loadMask){
18745             this.maskEl.hide();
18746         }
18747         
18748         this.store = Roo.factory(this.store, Roo.data);
18749         this.store.on('load', this.onLoad, this);
18750         this.store.on('beforeload', this.onBeforeLoad, this);
18751         
18752         this.resize();
18753         
18754         this.cells = this.el.select('.fc-day',true);
18755         //Roo.log(this.cells);
18756         this.textNodes = this.el.query('.fc-day-number');
18757         this.cells.addClassOnOver('fc-state-hover');
18758         
18759         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18760         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18761         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18762         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18763         
18764         this.on('monthchange', this.onMonthChange, this);
18765         
18766         this.update(new Date().clearTime());
18767     },
18768     
18769     resize : function() {
18770         var sz  = this.el.getSize();
18771         
18772         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18773         this.el.select('.fc-day-content div',true).setHeight(34);
18774     },
18775     
18776     
18777     // private
18778     showPrevMonth : function(e){
18779         this.update(this.activeDate.add("mo", -1));
18780     },
18781     showToday : function(e){
18782         this.update(new Date().clearTime());
18783     },
18784     // private
18785     showNextMonth : function(e){
18786         this.update(this.activeDate.add("mo", 1));
18787     },
18788
18789     // private
18790     showPrevYear : function(){
18791         this.update(this.activeDate.add("y", -1));
18792     },
18793
18794     // private
18795     showNextYear : function(){
18796         this.update(this.activeDate.add("y", 1));
18797     },
18798
18799     
18800    // private
18801     update : function(date)
18802     {
18803         var vd = this.activeDate;
18804         this.activeDate = date;
18805 //        if(vd && this.el){
18806 //            var t = date.getTime();
18807 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18808 //                Roo.log('using add remove');
18809 //                
18810 //                this.fireEvent('monthchange', this, date);
18811 //                
18812 //                this.cells.removeClass("fc-state-highlight");
18813 //                this.cells.each(function(c){
18814 //                   if(c.dateValue == t){
18815 //                       c.addClass("fc-state-highlight");
18816 //                       setTimeout(function(){
18817 //                            try{c.dom.firstChild.focus();}catch(e){}
18818 //                       }, 50);
18819 //                       return false;
18820 //                   }
18821 //                   return true;
18822 //                });
18823 //                return;
18824 //            }
18825 //        }
18826         
18827         var days = date.getDaysInMonth();
18828         
18829         var firstOfMonth = date.getFirstDateOfMonth();
18830         var startingPos = firstOfMonth.getDay()-this.startDay;
18831         
18832         if(startingPos < this.startDay){
18833             startingPos += 7;
18834         }
18835         
18836         var pm = date.add(Date.MONTH, -1);
18837         var prevStart = pm.getDaysInMonth()-startingPos;
18838 //        
18839         this.cells = this.el.select('.fc-day',true);
18840         this.textNodes = this.el.query('.fc-day-number');
18841         this.cells.addClassOnOver('fc-state-hover');
18842         
18843         var cells = this.cells.elements;
18844         var textEls = this.textNodes;
18845         
18846         Roo.each(cells, function(cell){
18847             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18848         });
18849         
18850         days += startingPos;
18851
18852         // convert everything to numbers so it's fast
18853         var day = 86400000;
18854         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18855         //Roo.log(d);
18856         //Roo.log(pm);
18857         //Roo.log(prevStart);
18858         
18859         var today = new Date().clearTime().getTime();
18860         var sel = date.clearTime().getTime();
18861         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18862         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18863         var ddMatch = this.disabledDatesRE;
18864         var ddText = this.disabledDatesText;
18865         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18866         var ddaysText = this.disabledDaysText;
18867         var format = this.format;
18868         
18869         var setCellClass = function(cal, cell){
18870             cell.row = 0;
18871             cell.events = [];
18872             cell.more = [];
18873             //Roo.log('set Cell Class');
18874             cell.title = "";
18875             var t = d.getTime();
18876             
18877             //Roo.log(d);
18878             
18879             cell.dateValue = t;
18880             if(t == today){
18881                 cell.className += " fc-today";
18882                 cell.className += " fc-state-highlight";
18883                 cell.title = cal.todayText;
18884             }
18885             if(t == sel){
18886                 // disable highlight in other month..
18887                 //cell.className += " fc-state-highlight";
18888                 
18889             }
18890             // disabling
18891             if(t < min) {
18892                 cell.className = " fc-state-disabled";
18893                 cell.title = cal.minText;
18894                 return;
18895             }
18896             if(t > max) {
18897                 cell.className = " fc-state-disabled";
18898                 cell.title = cal.maxText;
18899                 return;
18900             }
18901             if(ddays){
18902                 if(ddays.indexOf(d.getDay()) != -1){
18903                     cell.title = ddaysText;
18904                     cell.className = " fc-state-disabled";
18905                 }
18906             }
18907             if(ddMatch && format){
18908                 var fvalue = d.dateFormat(format);
18909                 if(ddMatch.test(fvalue)){
18910                     cell.title = ddText.replace("%0", fvalue);
18911                     cell.className = " fc-state-disabled";
18912                 }
18913             }
18914             
18915             if (!cell.initialClassName) {
18916                 cell.initialClassName = cell.dom.className;
18917             }
18918             
18919             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
18920         };
18921
18922         var i = 0;
18923         
18924         for(; i < startingPos; i++) {
18925             textEls[i].innerHTML = (++prevStart);
18926             d.setDate(d.getDate()+1);
18927             
18928             cells[i].className = "fc-past fc-other-month";
18929             setCellClass(this, cells[i]);
18930         }
18931         
18932         var intDay = 0;
18933         
18934         for(; i < days; i++){
18935             intDay = i - startingPos + 1;
18936             textEls[i].innerHTML = (intDay);
18937             d.setDate(d.getDate()+1);
18938             
18939             cells[i].className = ''; // "x-date-active";
18940             setCellClass(this, cells[i]);
18941         }
18942         var extraDays = 0;
18943         
18944         for(; i < 42; i++) {
18945             textEls[i].innerHTML = (++extraDays);
18946             d.setDate(d.getDate()+1);
18947             
18948             cells[i].className = "fc-future fc-other-month";
18949             setCellClass(this, cells[i]);
18950         }
18951         
18952         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
18953         
18954         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
18955         
18956         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
18957         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
18958         
18959         if(totalRows != 6){
18960             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
18961             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
18962         }
18963         
18964         this.fireEvent('monthchange', this, date);
18965         
18966         
18967         /*
18968         if(!this.internalRender){
18969             var main = this.el.dom.firstChild;
18970             var w = main.offsetWidth;
18971             this.el.setWidth(w + this.el.getBorderWidth("lr"));
18972             Roo.fly(main).setWidth(w);
18973             this.internalRender = true;
18974             // opera does not respect the auto grow header center column
18975             // then, after it gets a width opera refuses to recalculate
18976             // without a second pass
18977             if(Roo.isOpera && !this.secondPass){
18978                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
18979                 this.secondPass = true;
18980                 this.update.defer(10, this, [date]);
18981             }
18982         }
18983         */
18984         
18985     },
18986     
18987     findCell : function(dt) {
18988         dt = dt.clearTime().getTime();
18989         var ret = false;
18990         this.cells.each(function(c){
18991             //Roo.log("check " +c.dateValue + '?=' + dt);
18992             if(c.dateValue == dt){
18993                 ret = c;
18994                 return false;
18995             }
18996             return true;
18997         });
18998         
18999         return ret;
19000     },
19001     
19002     findCells : function(ev) {
19003         var s = ev.start.clone().clearTime().getTime();
19004        // Roo.log(s);
19005         var e= ev.end.clone().clearTime().getTime();
19006        // Roo.log(e);
19007         var ret = [];
19008         this.cells.each(function(c){
19009              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19010             
19011             if(c.dateValue > e){
19012                 return ;
19013             }
19014             if(c.dateValue < s){
19015                 return ;
19016             }
19017             ret.push(c);
19018         });
19019         
19020         return ret;    
19021     },
19022     
19023 //    findBestRow: function(cells)
19024 //    {
19025 //        var ret = 0;
19026 //        
19027 //        for (var i =0 ; i < cells.length;i++) {
19028 //            ret  = Math.max(cells[i].rows || 0,ret);
19029 //        }
19030 //        return ret;
19031 //        
19032 //    },
19033     
19034     
19035     addItem : function(ev)
19036     {
19037         // look for vertical location slot in
19038         var cells = this.findCells(ev);
19039         
19040 //        ev.row = this.findBestRow(cells);
19041         
19042         // work out the location.
19043         
19044         var crow = false;
19045         var rows = [];
19046         for(var i =0; i < cells.length; i++) {
19047             
19048             cells[i].row = cells[0].row;
19049             
19050             if(i == 0){
19051                 cells[i].row = cells[i].row + 1;
19052             }
19053             
19054             if (!crow) {
19055                 crow = {
19056                     start : cells[i],
19057                     end :  cells[i]
19058                 };
19059                 continue;
19060             }
19061             if (crow.start.getY() == cells[i].getY()) {
19062                 // on same row.
19063                 crow.end = cells[i];
19064                 continue;
19065             }
19066             // different row.
19067             rows.push(crow);
19068             crow = {
19069                 start: cells[i],
19070                 end : cells[i]
19071             };
19072             
19073         }
19074         
19075         rows.push(crow);
19076         ev.els = [];
19077         ev.rows = rows;
19078         ev.cells = cells;
19079         
19080         cells[0].events.push(ev);
19081         
19082         this.calevents.push(ev);
19083     },
19084     
19085     clearEvents: function() {
19086         
19087         if(!this.calevents){
19088             return;
19089         }
19090         
19091         Roo.each(this.cells.elements, function(c){
19092             c.row = 0;
19093             c.events = [];
19094             c.more = [];
19095         });
19096         
19097         Roo.each(this.calevents, function(e) {
19098             Roo.each(e.els, function(el) {
19099                 el.un('mouseenter' ,this.onEventEnter, this);
19100                 el.un('mouseleave' ,this.onEventLeave, this);
19101                 el.remove();
19102             },this);
19103         },this);
19104         
19105         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19106             e.remove();
19107         });
19108         
19109     },
19110     
19111     renderEvents: function()
19112     {   
19113         var _this = this;
19114         
19115         this.cells.each(function(c) {
19116             
19117             if(c.row < 5){
19118                 return;
19119             }
19120             
19121             var ev = c.events;
19122             
19123             var r = 4;
19124             if(c.row != c.events.length){
19125                 r = 4 - (4 - (c.row - c.events.length));
19126             }
19127             
19128             c.events = ev.slice(0, r);
19129             c.more = ev.slice(r);
19130             
19131             if(c.more.length && c.more.length == 1){
19132                 c.events.push(c.more.pop());
19133             }
19134             
19135             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19136             
19137         });
19138             
19139         this.cells.each(function(c) {
19140             
19141             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19142             
19143             
19144             for (var e = 0; e < c.events.length; e++){
19145                 var ev = c.events[e];
19146                 var rows = ev.rows;
19147                 
19148                 for(var i = 0; i < rows.length; i++) {
19149                 
19150                     // how many rows should it span..
19151
19152                     var  cfg = {
19153                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19154                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19155
19156                         unselectable : "on",
19157                         cn : [
19158                             {
19159                                 cls: 'fc-event-inner',
19160                                 cn : [
19161     //                                {
19162     //                                  tag:'span',
19163     //                                  cls: 'fc-event-time',
19164     //                                  html : cells.length > 1 ? '' : ev.time
19165     //                                },
19166                                     {
19167                                       tag:'span',
19168                                       cls: 'fc-event-title',
19169                                       html : String.format('{0}', ev.title)
19170                                     }
19171
19172
19173                                 ]
19174                             },
19175                             {
19176                                 cls: 'ui-resizable-handle ui-resizable-e',
19177                                 html : '&nbsp;&nbsp;&nbsp'
19178                             }
19179
19180                         ]
19181                     };
19182
19183                     if (i == 0) {
19184                         cfg.cls += ' fc-event-start';
19185                     }
19186                     if ((i+1) == rows.length) {
19187                         cfg.cls += ' fc-event-end';
19188                     }
19189
19190                     var ctr = _this.el.select('.fc-event-container',true).first();
19191                     var cg = ctr.createChild(cfg);
19192
19193                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19194                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19195
19196                     var r = (c.more.length) ? 1 : 0;
19197                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19198                     cg.setWidth(ebox.right - sbox.x -2);
19199
19200                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19201                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19202                     cg.on('click', _this.onEventClick, _this, ev);
19203
19204                     ev.els.push(cg);
19205                     
19206                 }
19207                 
19208             }
19209             
19210             
19211             if(c.more.length){
19212                 var  cfg = {
19213                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19214                     style : 'position: absolute',
19215                     unselectable : "on",
19216                     cn : [
19217                         {
19218                             cls: 'fc-event-inner',
19219                             cn : [
19220                                 {
19221                                   tag:'span',
19222                                   cls: 'fc-event-title',
19223                                   html : 'More'
19224                                 }
19225
19226
19227                             ]
19228                         },
19229                         {
19230                             cls: 'ui-resizable-handle ui-resizable-e',
19231                             html : '&nbsp;&nbsp;&nbsp'
19232                         }
19233
19234                     ]
19235                 };
19236
19237                 var ctr = _this.el.select('.fc-event-container',true).first();
19238                 var cg = ctr.createChild(cfg);
19239
19240                 var sbox = c.select('.fc-day-content',true).first().getBox();
19241                 var ebox = c.select('.fc-day-content',true).first().getBox();
19242                 //Roo.log(cg);
19243                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19244                 cg.setWidth(ebox.right - sbox.x -2);
19245
19246                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19247                 
19248             }
19249             
19250         });
19251         
19252         
19253         
19254     },
19255     
19256     onEventEnter: function (e, el,event,d) {
19257         this.fireEvent('evententer', this, el, event);
19258     },
19259     
19260     onEventLeave: function (e, el,event,d) {
19261         this.fireEvent('eventleave', this, el, event);
19262     },
19263     
19264     onEventClick: function (e, el,event,d) {
19265         this.fireEvent('eventclick', this, el, event);
19266     },
19267     
19268     onMonthChange: function () {
19269         this.store.load();
19270     },
19271     
19272     onMoreEventClick: function(e, el, more)
19273     {
19274         var _this = this;
19275         
19276         this.calpopover.placement = 'right';
19277         this.calpopover.setTitle('More');
19278         
19279         this.calpopover.setContent('');
19280         
19281         var ctr = this.calpopover.el.select('.popover-content', true).first();
19282         
19283         Roo.each(more, function(m){
19284             var cfg = {
19285                 cls : 'fc-event-hori fc-event-draggable',
19286                 html : m.title
19287             };
19288             var cg = ctr.createChild(cfg);
19289             
19290             cg.on('click', _this.onEventClick, _this, m);
19291         });
19292         
19293         this.calpopover.show(el);
19294         
19295         
19296     },
19297     
19298     onLoad: function () 
19299     {   
19300         this.calevents = [];
19301         var cal = this;
19302         
19303         if(this.store.getCount() > 0){
19304             this.store.data.each(function(d){
19305                cal.addItem({
19306                     id : d.data.id,
19307                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19308                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19309                     time : d.data.start_time,
19310                     title : d.data.title,
19311                     description : d.data.description,
19312                     venue : d.data.venue
19313                 });
19314             });
19315         }
19316         
19317         this.renderEvents();
19318         
19319         if(this.calevents.length && this.loadMask){
19320             this.maskEl.hide();
19321         }
19322     },
19323     
19324     onBeforeLoad: function()
19325     {
19326         this.clearEvents();
19327         if(this.loadMask){
19328             this.maskEl.show();
19329         }
19330     }
19331 });
19332
19333  
19334  /*
19335  * - LGPL
19336  *
19337  * element
19338  * 
19339  */
19340
19341 /**
19342  * @class Roo.bootstrap.Popover
19343  * @extends Roo.bootstrap.Component
19344  * Bootstrap Popover class
19345  * @cfg {String} html contents of the popover   (or false to use children..)
19346  * @cfg {String} title of popover (or false to hide)
19347  * @cfg {String} placement how it is placed
19348  * @cfg {String} trigger click || hover (or false to trigger manually)
19349  * @cfg {String} over what (parent or false to trigger manually.)
19350  * @cfg {Number} delay - delay before showing
19351  
19352  * @constructor
19353  * Create a new Popover
19354  * @param {Object} config The config object
19355  */
19356
19357 Roo.bootstrap.Popover = function(config){
19358     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19359     
19360     this.addEvents({
19361         // raw events
19362          /**
19363          * @event show
19364          * After the popover show
19365          * 
19366          * @param {Roo.bootstrap.Popover} this
19367          */
19368         "show" : true,
19369         /**
19370          * @event hide
19371          * After the popover hide
19372          * 
19373          * @param {Roo.bootstrap.Popover} this
19374          */
19375         "hide" : true
19376     });
19377 };
19378
19379 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19380     
19381     title: 'Fill in a title',
19382     html: false,
19383     
19384     placement : 'right',
19385     trigger : 'hover', // hover
19386     
19387     delay : 0,
19388     
19389     over: 'parent',
19390     
19391     can_build_overlaid : false,
19392     
19393     getChildContainer : function()
19394     {
19395         return this.el.select('.popover-content',true).first();
19396     },
19397     
19398     getAutoCreate : function(){
19399          
19400         var cfg = {
19401            cls : 'popover roo-dynamic',
19402            style: 'display:block',
19403            cn : [
19404                 {
19405                     cls : 'arrow'
19406                 },
19407                 {
19408                     cls : 'popover-inner',
19409                     cn : [
19410                         {
19411                             tag: 'h3',
19412                             cls: 'popover-title popover-header',
19413                             html : this.title
19414                         },
19415                         {
19416                             cls : 'popover-content popover-body',
19417                             html : this.html
19418                         }
19419                     ]
19420                     
19421                 }
19422            ]
19423         };
19424         
19425         return cfg;
19426     },
19427     setTitle: function(str)
19428     {
19429         this.title = str;
19430         this.el.select('.popover-title',true).first().dom.innerHTML = str;
19431     },
19432     setContent: function(str)
19433     {
19434         this.html = str;
19435         this.el.select('.popover-content',true).first().dom.innerHTML = str;
19436     },
19437     // as it get's added to the bottom of the page.
19438     onRender : function(ct, position)
19439     {
19440         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19441         if(!this.el){
19442             var cfg = Roo.apply({},  this.getAutoCreate());
19443             cfg.id = Roo.id();
19444             
19445             if (this.cls) {
19446                 cfg.cls += ' ' + this.cls;
19447             }
19448             if (this.style) {
19449                 cfg.style = this.style;
19450             }
19451             //Roo.log("adding to ");
19452             this.el = Roo.get(document.body).createChild(cfg, position);
19453 //            Roo.log(this.el);
19454         }
19455         this.initEvents();
19456     },
19457     
19458     initEvents : function()
19459     {
19460         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19461         this.el.enableDisplayMode('block');
19462         this.el.hide();
19463         if (this.over === false) {
19464             return; 
19465         }
19466         if (this.triggers === false) {
19467             return;
19468         }
19469         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19470         var triggers = this.trigger ? this.trigger.split(' ') : [];
19471         Roo.each(triggers, function(trigger) {
19472         
19473             if (trigger == 'click') {
19474                 on_el.on('click', this.toggle, this);
19475             } else if (trigger != 'manual') {
19476                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19477                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19478       
19479                 on_el.on(eventIn  ,this.enter, this);
19480                 on_el.on(eventOut, this.leave, this);
19481             }
19482         }, this);
19483         
19484     },
19485     
19486     
19487     // private
19488     timeout : null,
19489     hoverState : null,
19490     
19491     toggle : function () {
19492         this.hoverState == 'in' ? this.leave() : this.enter();
19493     },
19494     
19495     enter : function () {
19496         
19497         clearTimeout(this.timeout);
19498     
19499         this.hoverState = 'in';
19500     
19501         if (!this.delay || !this.delay.show) {
19502             this.show();
19503             return;
19504         }
19505         var _t = this;
19506         this.timeout = setTimeout(function () {
19507             if (_t.hoverState == 'in') {
19508                 _t.show();
19509             }
19510         }, this.delay.show)
19511     },
19512     
19513     leave : function() {
19514         clearTimeout(this.timeout);
19515     
19516         this.hoverState = 'out';
19517     
19518         if (!this.delay || !this.delay.hide) {
19519             this.hide();
19520             return;
19521         }
19522         var _t = this;
19523         this.timeout = setTimeout(function () {
19524             if (_t.hoverState == 'out') {
19525                 _t.hide();
19526             }
19527         }, this.delay.hide)
19528     },
19529     
19530     show : function (on_el)
19531     {
19532         if (!on_el) {
19533             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19534         }
19535         
19536         // set content.
19537         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19538         if (this.html !== false) {
19539             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19540         }
19541         this.el.removeClass([
19542             'fade','top','bottom', 'left', 'right','in',
19543             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19544         ]);
19545         if (!this.title.length) {
19546             this.el.select('.popover-title',true).hide();
19547         }
19548         
19549         var placement = typeof this.placement == 'function' ?
19550             this.placement.call(this, this.el, on_el) :
19551             this.placement;
19552             
19553         var autoToken = /\s?auto?\s?/i;
19554         var autoPlace = autoToken.test(placement);
19555         if (autoPlace) {
19556             placement = placement.replace(autoToken, '') || 'top';
19557         }
19558         
19559         //this.el.detach()
19560         //this.el.setXY([0,0]);
19561         this.el.show();
19562         this.el.dom.style.display='block';
19563         this.el.addClass(placement);
19564         
19565         //this.el.appendTo(on_el);
19566         
19567         var p = this.getPosition();
19568         var box = this.el.getBox();
19569         
19570         if (autoPlace) {
19571             // fixme..
19572         }
19573         var align = Roo.bootstrap.Popover.alignment[placement];
19574         
19575 //        Roo.log(align);
19576         this.el.alignTo(on_el, align[0],align[1]);
19577         //var arrow = this.el.select('.arrow',true).first();
19578         //arrow.set(align[2], 
19579         
19580         this.el.addClass('in');
19581         
19582         
19583         if (this.el.hasClass('fade')) {
19584             // fade it?
19585         }
19586         
19587         this.hoverState = 'in';
19588         
19589         this.fireEvent('show', this);
19590         
19591     },
19592     hide : function()
19593     {
19594         this.el.setXY([0,0]);
19595         this.el.removeClass('in');
19596         this.el.hide();
19597         this.hoverState = null;
19598         
19599         this.fireEvent('hide', this);
19600     }
19601     
19602 });
19603
19604 Roo.bootstrap.Popover.alignment = {
19605     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19606     'right' : ['l-r', [10,0], 'left bs-popover-left'],
19607     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19608     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19609 };
19610
19611  /*
19612  * - LGPL
19613  *
19614  * Progress
19615  * 
19616  */
19617
19618 /**
19619  * @class Roo.bootstrap.Progress
19620  * @extends Roo.bootstrap.Component
19621  * Bootstrap Progress class
19622  * @cfg {Boolean} striped striped of the progress bar
19623  * @cfg {Boolean} active animated of the progress bar
19624  * 
19625  * 
19626  * @constructor
19627  * Create a new Progress
19628  * @param {Object} config The config object
19629  */
19630
19631 Roo.bootstrap.Progress = function(config){
19632     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19633 };
19634
19635 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
19636     
19637     striped : false,
19638     active: false,
19639     
19640     getAutoCreate : function(){
19641         var cfg = {
19642             tag: 'div',
19643             cls: 'progress'
19644         };
19645         
19646         
19647         if(this.striped){
19648             cfg.cls += ' progress-striped';
19649         }
19650       
19651         if(this.active){
19652             cfg.cls += ' active';
19653         }
19654         
19655         
19656         return cfg;
19657     }
19658    
19659 });
19660
19661  
19662
19663  /*
19664  * - LGPL
19665  *
19666  * ProgressBar
19667  * 
19668  */
19669
19670 /**
19671  * @class Roo.bootstrap.ProgressBar
19672  * @extends Roo.bootstrap.Component
19673  * Bootstrap ProgressBar class
19674  * @cfg {Number} aria_valuenow aria-value now
19675  * @cfg {Number} aria_valuemin aria-value min
19676  * @cfg {Number} aria_valuemax aria-value max
19677  * @cfg {String} label label for the progress bar
19678  * @cfg {String} panel (success | info | warning | danger )
19679  * @cfg {String} role role of the progress bar
19680  * @cfg {String} sr_only text
19681  * 
19682  * 
19683  * @constructor
19684  * Create a new ProgressBar
19685  * @param {Object} config The config object
19686  */
19687
19688 Roo.bootstrap.ProgressBar = function(config){
19689     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19690 };
19691
19692 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
19693     
19694     aria_valuenow : 0,
19695     aria_valuemin : 0,
19696     aria_valuemax : 100,
19697     label : false,
19698     panel : false,
19699     role : false,
19700     sr_only: false,
19701     
19702     getAutoCreate : function()
19703     {
19704         
19705         var cfg = {
19706             tag: 'div',
19707             cls: 'progress-bar',
19708             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19709         };
19710         
19711         if(this.sr_only){
19712             cfg.cn = {
19713                 tag: 'span',
19714                 cls: 'sr-only',
19715                 html: this.sr_only
19716             }
19717         }
19718         
19719         if(this.role){
19720             cfg.role = this.role;
19721         }
19722         
19723         if(this.aria_valuenow){
19724             cfg['aria-valuenow'] = this.aria_valuenow;
19725         }
19726         
19727         if(this.aria_valuemin){
19728             cfg['aria-valuemin'] = this.aria_valuemin;
19729         }
19730         
19731         if(this.aria_valuemax){
19732             cfg['aria-valuemax'] = this.aria_valuemax;
19733         }
19734         
19735         if(this.label && !this.sr_only){
19736             cfg.html = this.label;
19737         }
19738         
19739         if(this.panel){
19740             cfg.cls += ' progress-bar-' + this.panel;
19741         }
19742         
19743         return cfg;
19744     },
19745     
19746     update : function(aria_valuenow)
19747     {
19748         this.aria_valuenow = aria_valuenow;
19749         
19750         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19751     }
19752    
19753 });
19754
19755  
19756
19757  /*
19758  * - LGPL
19759  *
19760  * column
19761  * 
19762  */
19763
19764 /**
19765  * @class Roo.bootstrap.TabGroup
19766  * @extends Roo.bootstrap.Column
19767  * Bootstrap Column class
19768  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19769  * @cfg {Boolean} carousel true to make the group behave like a carousel
19770  * @cfg {Boolean} bullets show bullets for the panels
19771  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19772  * @cfg {Number} timer auto slide timer .. default 0 millisecond
19773  * @cfg {Boolean} showarrow (true|false) show arrow default true
19774  * 
19775  * @constructor
19776  * Create a new TabGroup
19777  * @param {Object} config The config object
19778  */
19779
19780 Roo.bootstrap.TabGroup = function(config){
19781     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19782     if (!this.navId) {
19783         this.navId = Roo.id();
19784     }
19785     this.tabs = [];
19786     Roo.bootstrap.TabGroup.register(this);
19787     
19788 };
19789
19790 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
19791     
19792     carousel : false,
19793     transition : false,
19794     bullets : 0,
19795     timer : 0,
19796     autoslide : false,
19797     slideFn : false,
19798     slideOnTouch : false,
19799     showarrow : true,
19800     
19801     getAutoCreate : function()
19802     {
19803         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19804         
19805         cfg.cls += ' tab-content';
19806         
19807         if (this.carousel) {
19808             cfg.cls += ' carousel slide';
19809             
19810             cfg.cn = [{
19811                cls : 'carousel-inner',
19812                cn : []
19813             }];
19814         
19815             if(this.bullets  && !Roo.isTouch){
19816                 
19817                 var bullets = {
19818                     cls : 'carousel-bullets',
19819                     cn : []
19820                 };
19821                
19822                 if(this.bullets_cls){
19823                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19824                 }
19825                 
19826                 bullets.cn.push({
19827                     cls : 'clear'
19828                 });
19829                 
19830                 cfg.cn[0].cn.push(bullets);
19831             }
19832             
19833             if(this.showarrow){
19834                 cfg.cn[0].cn.push({
19835                     tag : 'div',
19836                     class : 'carousel-arrow',
19837                     cn : [
19838                         {
19839                             tag : 'div',
19840                             class : 'carousel-prev',
19841                             cn : [
19842                                 {
19843                                     tag : 'i',
19844                                     class : 'fa fa-chevron-left'
19845                                 }
19846                             ]
19847                         },
19848                         {
19849                             tag : 'div',
19850                             class : 'carousel-next',
19851                             cn : [
19852                                 {
19853                                     tag : 'i',
19854                                     class : 'fa fa-chevron-right'
19855                                 }
19856                             ]
19857                         }
19858                     ]
19859                 });
19860             }
19861             
19862         }
19863         
19864         return cfg;
19865     },
19866     
19867     initEvents:  function()
19868     {
19869 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19870 //            this.el.on("touchstart", this.onTouchStart, this);
19871 //        }
19872         
19873         if(this.autoslide){
19874             var _this = this;
19875             
19876             this.slideFn = window.setInterval(function() {
19877                 _this.showPanelNext();
19878             }, this.timer);
19879         }
19880         
19881         if(this.showarrow){
19882             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
19883             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
19884         }
19885         
19886         
19887     },
19888     
19889 //    onTouchStart : function(e, el, o)
19890 //    {
19891 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
19892 //            return;
19893 //        }
19894 //        
19895 //        this.showPanelNext();
19896 //    },
19897     
19898     
19899     getChildContainer : function()
19900     {
19901         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
19902     },
19903     
19904     /**
19905     * register a Navigation item
19906     * @param {Roo.bootstrap.NavItem} the navitem to add
19907     */
19908     register : function(item)
19909     {
19910         this.tabs.push( item);
19911         item.navId = this.navId; // not really needed..
19912         this.addBullet();
19913     
19914     },
19915     
19916     getActivePanel : function()
19917     {
19918         var r = false;
19919         Roo.each(this.tabs, function(t) {
19920             if (t.active) {
19921                 r = t;
19922                 return false;
19923             }
19924             return null;
19925         });
19926         return r;
19927         
19928     },
19929     getPanelByName : function(n)
19930     {
19931         var r = false;
19932         Roo.each(this.tabs, function(t) {
19933             if (t.tabId == n) {
19934                 r = t;
19935                 return false;
19936             }
19937             return null;
19938         });
19939         return r;
19940     },
19941     indexOfPanel : function(p)
19942     {
19943         var r = false;
19944         Roo.each(this.tabs, function(t,i) {
19945             if (t.tabId == p.tabId) {
19946                 r = i;
19947                 return false;
19948             }
19949             return null;
19950         });
19951         return r;
19952     },
19953     /**
19954      * show a specific panel
19955      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
19956      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
19957      */
19958     showPanel : function (pan)
19959     {
19960         if(this.transition || typeof(pan) == 'undefined'){
19961             Roo.log("waiting for the transitionend");
19962             return false;
19963         }
19964         
19965         if (typeof(pan) == 'number') {
19966             pan = this.tabs[pan];
19967         }
19968         
19969         if (typeof(pan) == 'string') {
19970             pan = this.getPanelByName(pan);
19971         }
19972         
19973         var cur = this.getActivePanel();
19974         
19975         if(!pan || !cur){
19976             Roo.log('pan or acitve pan is undefined');
19977             return false;
19978         }
19979         
19980         if (pan.tabId == this.getActivePanel().tabId) {
19981             return true;
19982         }
19983         
19984         if (false === cur.fireEvent('beforedeactivate')) {
19985             return false;
19986         }
19987         
19988         if(this.bullets > 0 && !Roo.isTouch){
19989             this.setActiveBullet(this.indexOfPanel(pan));
19990         }
19991         
19992         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
19993             
19994             //class="carousel-item carousel-item-next carousel-item-left"
19995             
19996             this.transition = true;
19997             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
19998             var lr = dir == 'next' ? 'left' : 'right';
19999             pan.el.addClass(dir); // or prev
20000             pan.el.addClass('carousel-item-' + dir); // or prev
20001             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20002             cur.el.addClass(lr); // or right
20003             pan.el.addClass(lr);
20004             cur.el.addClass('carousel-item-' +lr); // or right
20005             pan.el.addClass('carousel-item-' +lr);
20006             
20007             
20008             var _this = this;
20009             cur.el.on('transitionend', function() {
20010                 Roo.log("trans end?");
20011                 
20012                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20013                 pan.setActive(true);
20014                 
20015                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20016                 cur.setActive(false);
20017                 
20018                 _this.transition = false;
20019                 
20020             }, this, { single:  true } );
20021             
20022             return true;
20023         }
20024         
20025         cur.setActive(false);
20026         pan.setActive(true);
20027         
20028         return true;
20029         
20030     },
20031     showPanelNext : function()
20032     {
20033         var i = this.indexOfPanel(this.getActivePanel());
20034         
20035         if (i >= this.tabs.length - 1 && !this.autoslide) {
20036             return;
20037         }
20038         
20039         if (i >= this.tabs.length - 1 && this.autoslide) {
20040             i = -1;
20041         }
20042         
20043         this.showPanel(this.tabs[i+1]);
20044     },
20045     
20046     showPanelPrev : function()
20047     {
20048         var i = this.indexOfPanel(this.getActivePanel());
20049         
20050         if (i  < 1 && !this.autoslide) {
20051             return;
20052         }
20053         
20054         if (i < 1 && this.autoslide) {
20055             i = this.tabs.length;
20056         }
20057         
20058         this.showPanel(this.tabs[i-1]);
20059     },
20060     
20061     
20062     addBullet: function()
20063     {
20064         if(!this.bullets || Roo.isTouch){
20065             return;
20066         }
20067         var ctr = this.el.select('.carousel-bullets',true).first();
20068         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20069         var bullet = ctr.createChild({
20070             cls : 'bullet bullet-' + i
20071         },ctr.dom.lastChild);
20072         
20073         
20074         var _this = this;
20075         
20076         bullet.on('click', (function(e, el, o, ii, t){
20077
20078             e.preventDefault();
20079
20080             this.showPanel(ii);
20081
20082             if(this.autoslide && this.slideFn){
20083                 clearInterval(this.slideFn);
20084                 this.slideFn = window.setInterval(function() {
20085                     _this.showPanelNext();
20086                 }, this.timer);
20087             }
20088
20089         }).createDelegate(this, [i, bullet], true));
20090                 
20091         
20092     },
20093      
20094     setActiveBullet : function(i)
20095     {
20096         if(Roo.isTouch){
20097             return;
20098         }
20099         
20100         Roo.each(this.el.select('.bullet', true).elements, function(el){
20101             el.removeClass('selected');
20102         });
20103
20104         var bullet = this.el.select('.bullet-' + i, true).first();
20105         
20106         if(!bullet){
20107             return;
20108         }
20109         
20110         bullet.addClass('selected');
20111     }
20112     
20113     
20114   
20115 });
20116
20117  
20118
20119  
20120  
20121 Roo.apply(Roo.bootstrap.TabGroup, {
20122     
20123     groups: {},
20124      /**
20125     * register a Navigation Group
20126     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20127     */
20128     register : function(navgrp)
20129     {
20130         this.groups[navgrp.navId] = navgrp;
20131         
20132     },
20133     /**
20134     * fetch a Navigation Group based on the navigation ID
20135     * if one does not exist , it will get created.
20136     * @param {string} the navgroup to add
20137     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20138     */
20139     get: function(navId) {
20140         if (typeof(this.groups[navId]) == 'undefined') {
20141             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20142         }
20143         return this.groups[navId] ;
20144     }
20145     
20146     
20147     
20148 });
20149
20150  /*
20151  * - LGPL
20152  *
20153  * TabPanel
20154  * 
20155  */
20156
20157 /**
20158  * @class Roo.bootstrap.TabPanel
20159  * @extends Roo.bootstrap.Component
20160  * Bootstrap TabPanel class
20161  * @cfg {Boolean} active panel active
20162  * @cfg {String} html panel content
20163  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20164  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20165  * @cfg {String} href click to link..
20166  * 
20167  * 
20168  * @constructor
20169  * Create a new TabPanel
20170  * @param {Object} config The config object
20171  */
20172
20173 Roo.bootstrap.TabPanel = function(config){
20174     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20175     this.addEvents({
20176         /**
20177              * @event changed
20178              * Fires when the active status changes
20179              * @param {Roo.bootstrap.TabPanel} this
20180              * @param {Boolean} state the new state
20181             
20182          */
20183         'changed': true,
20184         /**
20185              * @event beforedeactivate
20186              * Fires before a tab is de-activated - can be used to do validation on a form.
20187              * @param {Roo.bootstrap.TabPanel} this
20188              * @return {Boolean} false if there is an error
20189             
20190          */
20191         'beforedeactivate': true
20192      });
20193     
20194     this.tabId = this.tabId || Roo.id();
20195   
20196 };
20197
20198 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20199     
20200     active: false,
20201     html: false,
20202     tabId: false,
20203     navId : false,
20204     href : '',
20205     
20206     getAutoCreate : function(){
20207         
20208         
20209         var cfg = {
20210             tag: 'div',
20211             // item is needed for carousel - not sure if it has any effect otherwise
20212             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20213             html: this.html || ''
20214         };
20215         
20216         if(this.active){
20217             cfg.cls += ' active';
20218         }
20219         
20220         if(this.tabId){
20221             cfg.tabId = this.tabId;
20222         }
20223         
20224         
20225         
20226         return cfg;
20227     },
20228     
20229     initEvents:  function()
20230     {
20231         var p = this.parent();
20232         
20233         this.navId = this.navId || p.navId;
20234         
20235         if (typeof(this.navId) != 'undefined') {
20236             // not really needed.. but just in case.. parent should be a NavGroup.
20237             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20238             
20239             tg.register(this);
20240             
20241             var i = tg.tabs.length - 1;
20242             
20243             if(this.active && tg.bullets > 0 && i < tg.bullets){
20244                 tg.setActiveBullet(i);
20245             }
20246         }
20247         
20248         this.el.on('click', this.onClick, this);
20249         
20250         if(Roo.isTouch){
20251             this.el.on("touchstart", this.onTouchStart, this);
20252             this.el.on("touchmove", this.onTouchMove, this);
20253             this.el.on("touchend", this.onTouchEnd, this);
20254         }
20255         
20256     },
20257     
20258     onRender : function(ct, position)
20259     {
20260         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20261     },
20262     
20263     setActive : function(state)
20264     {
20265         Roo.log("panel - set active " + this.tabId + "=" + state);
20266         
20267         this.active = state;
20268         if (!state) {
20269             this.el.removeClass('active');
20270             
20271         } else  if (!this.el.hasClass('active')) {
20272             this.el.addClass('active');
20273         }
20274         
20275         this.fireEvent('changed', this, state);
20276     },
20277     
20278     onClick : function(e)
20279     {
20280         e.preventDefault();
20281         
20282         if(!this.href.length){
20283             return;
20284         }
20285         
20286         window.location.href = this.href;
20287     },
20288     
20289     startX : 0,
20290     startY : 0,
20291     endX : 0,
20292     endY : 0,
20293     swiping : false,
20294     
20295     onTouchStart : function(e)
20296     {
20297         this.swiping = false;
20298         
20299         this.startX = e.browserEvent.touches[0].clientX;
20300         this.startY = e.browserEvent.touches[0].clientY;
20301     },
20302     
20303     onTouchMove : function(e)
20304     {
20305         this.swiping = true;
20306         
20307         this.endX = e.browserEvent.touches[0].clientX;
20308         this.endY = e.browserEvent.touches[0].clientY;
20309     },
20310     
20311     onTouchEnd : function(e)
20312     {
20313         if(!this.swiping){
20314             this.onClick(e);
20315             return;
20316         }
20317         
20318         var tabGroup = this.parent();
20319         
20320         if(this.endX > this.startX){ // swiping right
20321             tabGroup.showPanelPrev();
20322             return;
20323         }
20324         
20325         if(this.startX > this.endX){ // swiping left
20326             tabGroup.showPanelNext();
20327             return;
20328         }
20329     }
20330     
20331     
20332 });
20333  
20334
20335  
20336
20337  /*
20338  * - LGPL
20339  *
20340  * DateField
20341  * 
20342  */
20343
20344 /**
20345  * @class Roo.bootstrap.DateField
20346  * @extends Roo.bootstrap.Input
20347  * Bootstrap DateField class
20348  * @cfg {Number} weekStart default 0
20349  * @cfg {String} viewMode default empty, (months|years)
20350  * @cfg {String} minViewMode default empty, (months|years)
20351  * @cfg {Number} startDate default -Infinity
20352  * @cfg {Number} endDate default Infinity
20353  * @cfg {Boolean} todayHighlight default false
20354  * @cfg {Boolean} todayBtn default false
20355  * @cfg {Boolean} calendarWeeks default false
20356  * @cfg {Object} daysOfWeekDisabled default empty
20357  * @cfg {Boolean} singleMode default false (true | false)
20358  * 
20359  * @cfg {Boolean} keyboardNavigation default true
20360  * @cfg {String} language default en
20361  * 
20362  * @constructor
20363  * Create a new DateField
20364  * @param {Object} config The config object
20365  */
20366
20367 Roo.bootstrap.DateField = function(config){
20368     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20369      this.addEvents({
20370             /**
20371              * @event show
20372              * Fires when this field show.
20373              * @param {Roo.bootstrap.DateField} this
20374              * @param {Mixed} date The date value
20375              */
20376             show : true,
20377             /**
20378              * @event show
20379              * Fires when this field hide.
20380              * @param {Roo.bootstrap.DateField} this
20381              * @param {Mixed} date The date value
20382              */
20383             hide : true,
20384             /**
20385              * @event select
20386              * Fires when select a date.
20387              * @param {Roo.bootstrap.DateField} this
20388              * @param {Mixed} date The date value
20389              */
20390             select : true,
20391             /**
20392              * @event beforeselect
20393              * Fires when before select a date.
20394              * @param {Roo.bootstrap.DateField} this
20395              * @param {Mixed} date The date value
20396              */
20397             beforeselect : true
20398         });
20399 };
20400
20401 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20402     
20403     /**
20404      * @cfg {String} format
20405      * The default date format string which can be overriden for localization support.  The format must be
20406      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20407      */
20408     format : "m/d/y",
20409     /**
20410      * @cfg {String} altFormats
20411      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20412      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20413      */
20414     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20415     
20416     weekStart : 0,
20417     
20418     viewMode : '',
20419     
20420     minViewMode : '',
20421     
20422     todayHighlight : false,
20423     
20424     todayBtn: false,
20425     
20426     language: 'en',
20427     
20428     keyboardNavigation: true,
20429     
20430     calendarWeeks: false,
20431     
20432     startDate: -Infinity,
20433     
20434     endDate: Infinity,
20435     
20436     daysOfWeekDisabled: [],
20437     
20438     _events: [],
20439     
20440     singleMode : false,
20441     
20442     UTCDate: function()
20443     {
20444         return new Date(Date.UTC.apply(Date, arguments));
20445     },
20446     
20447     UTCToday: function()
20448     {
20449         var today = new Date();
20450         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20451     },
20452     
20453     getDate: function() {
20454             var d = this.getUTCDate();
20455             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20456     },
20457     
20458     getUTCDate: function() {
20459             return this.date;
20460     },
20461     
20462     setDate: function(d) {
20463             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20464     },
20465     
20466     setUTCDate: function(d) {
20467             this.date = d;
20468             this.setValue(this.formatDate(this.date));
20469     },
20470         
20471     onRender: function(ct, position)
20472     {
20473         
20474         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20475         
20476         this.language = this.language || 'en';
20477         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20478         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20479         
20480         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20481         this.format = this.format || 'm/d/y';
20482         this.isInline = false;
20483         this.isInput = true;
20484         this.component = this.el.select('.add-on', true).first() || false;
20485         this.component = (this.component && this.component.length === 0) ? false : this.component;
20486         this.hasInput = this.component && this.inputEl().length;
20487         
20488         if (typeof(this.minViewMode === 'string')) {
20489             switch (this.minViewMode) {
20490                 case 'months':
20491                     this.minViewMode = 1;
20492                     break;
20493                 case 'years':
20494                     this.minViewMode = 2;
20495                     break;
20496                 default:
20497                     this.minViewMode = 0;
20498                     break;
20499             }
20500         }
20501         
20502         if (typeof(this.viewMode === 'string')) {
20503             switch (this.viewMode) {
20504                 case 'months':
20505                     this.viewMode = 1;
20506                     break;
20507                 case 'years':
20508                     this.viewMode = 2;
20509                     break;
20510                 default:
20511                     this.viewMode = 0;
20512                     break;
20513             }
20514         }
20515                 
20516         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20517         
20518 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20519         
20520         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20521         
20522         this.picker().on('mousedown', this.onMousedown, this);
20523         this.picker().on('click', this.onClick, this);
20524         
20525         this.picker().addClass('datepicker-dropdown');
20526         
20527         this.startViewMode = this.viewMode;
20528         
20529         if(this.singleMode){
20530             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20531                 v.setVisibilityMode(Roo.Element.DISPLAY);
20532                 v.hide();
20533             });
20534             
20535             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20536                 v.setStyle('width', '189px');
20537             });
20538         }
20539         
20540         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20541             if(!this.calendarWeeks){
20542                 v.remove();
20543                 return;
20544             }
20545             
20546             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20547             v.attr('colspan', function(i, val){
20548                 return parseInt(val) + 1;
20549             });
20550         });
20551                         
20552         
20553         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20554         
20555         this.setStartDate(this.startDate);
20556         this.setEndDate(this.endDate);
20557         
20558         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20559         
20560         this.fillDow();
20561         this.fillMonths();
20562         this.update();
20563         this.showMode();
20564         
20565         if(this.isInline) {
20566             this.showPopup();
20567         }
20568     },
20569     
20570     picker : function()
20571     {
20572         return this.pickerEl;
20573 //        return this.el.select('.datepicker', true).first();
20574     },
20575     
20576     fillDow: function()
20577     {
20578         var dowCnt = this.weekStart;
20579         
20580         var dow = {
20581             tag: 'tr',
20582             cn: [
20583                 
20584             ]
20585         };
20586         
20587         if(this.calendarWeeks){
20588             dow.cn.push({
20589                 tag: 'th',
20590                 cls: 'cw',
20591                 html: '&nbsp;'
20592             })
20593         }
20594         
20595         while (dowCnt < this.weekStart + 7) {
20596             dow.cn.push({
20597                 tag: 'th',
20598                 cls: 'dow',
20599                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20600             });
20601         }
20602         
20603         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20604     },
20605     
20606     fillMonths: function()
20607     {    
20608         var i = 0;
20609         var months = this.picker().select('>.datepicker-months td', true).first();
20610         
20611         months.dom.innerHTML = '';
20612         
20613         while (i < 12) {
20614             var month = {
20615                 tag: 'span',
20616                 cls: 'month',
20617                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20618             };
20619             
20620             months.createChild(month);
20621         }
20622         
20623     },
20624     
20625     update: function()
20626     {
20627         this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
20628         
20629         if (this.date < this.startDate) {
20630             this.viewDate = new Date(this.startDate);
20631         } else if (this.date > this.endDate) {
20632             this.viewDate = new Date(this.endDate);
20633         } else {
20634             this.viewDate = new Date(this.date);
20635         }
20636         
20637         this.fill();
20638     },
20639     
20640     fill: function() 
20641     {
20642         var d = new Date(this.viewDate),
20643                 year = d.getUTCFullYear(),
20644                 month = d.getUTCMonth(),
20645                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20646                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20647                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20648                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20649                 currentDate = this.date && this.date.valueOf(),
20650                 today = this.UTCToday();
20651         
20652         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20653         
20654 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20655         
20656 //        this.picker.select('>tfoot th.today').
20657 //                                              .text(dates[this.language].today)
20658 //                                              .toggle(this.todayBtn !== false);
20659     
20660         this.updateNavArrows();
20661         this.fillMonths();
20662                                                 
20663         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20664         
20665         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20666          
20667         prevMonth.setUTCDate(day);
20668         
20669         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20670         
20671         var nextMonth = new Date(prevMonth);
20672         
20673         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20674         
20675         nextMonth = nextMonth.valueOf();
20676         
20677         var fillMonths = false;
20678         
20679         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20680         
20681         while(prevMonth.valueOf() <= nextMonth) {
20682             var clsName = '';
20683             
20684             if (prevMonth.getUTCDay() === this.weekStart) {
20685                 if(fillMonths){
20686                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20687                 }
20688                     
20689                 fillMonths = {
20690                     tag: 'tr',
20691                     cn: []
20692                 };
20693                 
20694                 if(this.calendarWeeks){
20695                     // ISO 8601: First week contains first thursday.
20696                     // ISO also states week starts on Monday, but we can be more abstract here.
20697                     var
20698                     // Start of current week: based on weekstart/current date
20699                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20700                     // Thursday of this week
20701                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20702                     // First Thursday of year, year from thursday
20703                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20704                     // Calendar week: ms between thursdays, div ms per day, div 7 days
20705                     calWeek =  (th - yth) / 864e5 / 7 + 1;
20706                     
20707                     fillMonths.cn.push({
20708                         tag: 'td',
20709                         cls: 'cw',
20710                         html: calWeek
20711                     });
20712                 }
20713             }
20714             
20715             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20716                 clsName += ' old';
20717             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20718                 clsName += ' new';
20719             }
20720             if (this.todayHighlight &&
20721                 prevMonth.getUTCFullYear() == today.getFullYear() &&
20722                 prevMonth.getUTCMonth() == today.getMonth() &&
20723                 prevMonth.getUTCDate() == today.getDate()) {
20724                 clsName += ' today';
20725             }
20726             
20727             if (currentDate && prevMonth.valueOf() === currentDate) {
20728                 clsName += ' active';
20729             }
20730             
20731             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20732                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20733                     clsName += ' disabled';
20734             }
20735             
20736             fillMonths.cn.push({
20737                 tag: 'td',
20738                 cls: 'day ' + clsName,
20739                 html: prevMonth.getDate()
20740             });
20741             
20742             prevMonth.setDate(prevMonth.getDate()+1);
20743         }
20744           
20745         var currentYear = this.date && this.date.getUTCFullYear();
20746         var currentMonth = this.date && this.date.getUTCMonth();
20747         
20748         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20749         
20750         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20751             v.removeClass('active');
20752             
20753             if(currentYear === year && k === currentMonth){
20754                 v.addClass('active');
20755             }
20756             
20757             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20758                 v.addClass('disabled');
20759             }
20760             
20761         });
20762         
20763         
20764         year = parseInt(year/10, 10) * 10;
20765         
20766         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20767         
20768         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20769         
20770         year -= 1;
20771         for (var i = -1; i < 11; i++) {
20772             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20773                 tag: 'span',
20774                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20775                 html: year
20776             });
20777             
20778             year += 1;
20779         }
20780     },
20781     
20782     showMode: function(dir) 
20783     {
20784         if (dir) {
20785             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20786         }
20787         
20788         Roo.each(this.picker().select('>div',true).elements, function(v){
20789             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20790             v.hide();
20791         });
20792         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20793     },
20794     
20795     place: function()
20796     {
20797         if(this.isInline) {
20798             return;
20799         }
20800         
20801         this.picker().removeClass(['bottom', 'top']);
20802         
20803         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20804             /*
20805              * place to the top of element!
20806              *
20807              */
20808             
20809             this.picker().addClass('top');
20810             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20811             
20812             return;
20813         }
20814         
20815         this.picker().addClass('bottom');
20816         
20817         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20818     },
20819     
20820     parseDate : function(value)
20821     {
20822         if(!value || value instanceof Date){
20823             return value;
20824         }
20825         var v = Date.parseDate(value, this.format);
20826         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20827             v = Date.parseDate(value, 'Y-m-d');
20828         }
20829         if(!v && this.altFormats){
20830             if(!this.altFormatsArray){
20831                 this.altFormatsArray = this.altFormats.split("|");
20832             }
20833             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20834                 v = Date.parseDate(value, this.altFormatsArray[i]);
20835             }
20836         }
20837         return v;
20838     },
20839     
20840     formatDate : function(date, fmt)
20841     {   
20842         return (!date || !(date instanceof Date)) ?
20843         date : date.dateFormat(fmt || this.format);
20844     },
20845     
20846     onFocus : function()
20847     {
20848         Roo.bootstrap.DateField.superclass.onFocus.call(this);
20849         this.showPopup();
20850     },
20851     
20852     onBlur : function()
20853     {
20854         Roo.bootstrap.DateField.superclass.onBlur.call(this);
20855         
20856         var d = this.inputEl().getValue();
20857         
20858         this.setValue(d);
20859                 
20860         this.hidePopup();
20861     },
20862     
20863     showPopup : function()
20864     {
20865         this.picker().show();
20866         this.update();
20867         this.place();
20868         
20869         this.fireEvent('showpopup', this, this.date);
20870     },
20871     
20872     hidePopup : function()
20873     {
20874         if(this.isInline) {
20875             return;
20876         }
20877         this.picker().hide();
20878         this.viewMode = this.startViewMode;
20879         this.showMode();
20880         
20881         this.fireEvent('hidepopup', this, this.date);
20882         
20883     },
20884     
20885     onMousedown: function(e)
20886     {
20887         e.stopPropagation();
20888         e.preventDefault();
20889     },
20890     
20891     keyup: function(e)
20892     {
20893         Roo.bootstrap.DateField.superclass.keyup.call(this);
20894         this.update();
20895     },
20896
20897     setValue: function(v)
20898     {
20899         if(this.fireEvent('beforeselect', this, v) !== false){
20900             var d = new Date(this.parseDate(v) ).clearTime();
20901         
20902             if(isNaN(d.getTime())){
20903                 this.date = this.viewDate = '';
20904                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20905                 return;
20906             }
20907
20908             v = this.formatDate(d);
20909
20910             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
20911
20912             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
20913
20914             this.update();
20915
20916             this.fireEvent('select', this, this.date);
20917         }
20918     },
20919     
20920     getValue: function()
20921     {
20922         return this.formatDate(this.date);
20923     },
20924     
20925     fireKey: function(e)
20926     {
20927         if (!this.picker().isVisible()){
20928             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20929                 this.showPopup();
20930             }
20931             return;
20932         }
20933         
20934         var dateChanged = false,
20935         dir, day, month,
20936         newDate, newViewDate;
20937         
20938         switch(e.keyCode){
20939             case 27: // escape
20940                 this.hidePopup();
20941                 e.preventDefault();
20942                 break;
20943             case 37: // left
20944             case 39: // right
20945                 if (!this.keyboardNavigation) {
20946                     break;
20947                 }
20948                 dir = e.keyCode == 37 ? -1 : 1;
20949                 
20950                 if (e.ctrlKey){
20951                     newDate = this.moveYear(this.date, dir);
20952                     newViewDate = this.moveYear(this.viewDate, dir);
20953                 } else if (e.shiftKey){
20954                     newDate = this.moveMonth(this.date, dir);
20955                     newViewDate = this.moveMonth(this.viewDate, dir);
20956                 } else {
20957                     newDate = new Date(this.date);
20958                     newDate.setUTCDate(this.date.getUTCDate() + dir);
20959                     newViewDate = new Date(this.viewDate);
20960                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
20961                 }
20962                 if (this.dateWithinRange(newDate)){
20963                     this.date = newDate;
20964                     this.viewDate = newViewDate;
20965                     this.setValue(this.formatDate(this.date));
20966 //                    this.update();
20967                     e.preventDefault();
20968                     dateChanged = true;
20969                 }
20970                 break;
20971             case 38: // up
20972             case 40: // down
20973                 if (!this.keyboardNavigation) {
20974                     break;
20975                 }
20976                 dir = e.keyCode == 38 ? -1 : 1;
20977                 if (e.ctrlKey){
20978                     newDate = this.moveYear(this.date, dir);
20979                     newViewDate = this.moveYear(this.viewDate, dir);
20980                 } else if (e.shiftKey){
20981                     newDate = this.moveMonth(this.date, dir);
20982                     newViewDate = this.moveMonth(this.viewDate, dir);
20983                 } else {
20984                     newDate = new Date(this.date);
20985                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
20986                     newViewDate = new Date(this.viewDate);
20987                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
20988                 }
20989                 if (this.dateWithinRange(newDate)){
20990                     this.date = newDate;
20991                     this.viewDate = newViewDate;
20992                     this.setValue(this.formatDate(this.date));
20993 //                    this.update();
20994                     e.preventDefault();
20995                     dateChanged = true;
20996                 }
20997                 break;
20998             case 13: // enter
20999                 this.setValue(this.formatDate(this.date));
21000                 this.hidePopup();
21001                 e.preventDefault();
21002                 break;
21003             case 9: // tab
21004                 this.setValue(this.formatDate(this.date));
21005                 this.hidePopup();
21006                 break;
21007             case 16: // shift
21008             case 17: // ctrl
21009             case 18: // alt
21010                 break;
21011             default :
21012                 this.hidePopup();
21013                 
21014         }
21015     },
21016     
21017     
21018     onClick: function(e) 
21019     {
21020         e.stopPropagation();
21021         e.preventDefault();
21022         
21023         var target = e.getTarget();
21024         
21025         if(target.nodeName.toLowerCase() === 'i'){
21026             target = Roo.get(target).dom.parentNode;
21027         }
21028         
21029         var nodeName = target.nodeName;
21030         var className = target.className;
21031         var html = target.innerHTML;
21032         //Roo.log(nodeName);
21033         
21034         switch(nodeName.toLowerCase()) {
21035             case 'th':
21036                 switch(className) {
21037                     case 'switch':
21038                         this.showMode(1);
21039                         break;
21040                     case 'prev':
21041                     case 'next':
21042                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21043                         switch(this.viewMode){
21044                                 case 0:
21045                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21046                                         break;
21047                                 case 1:
21048                                 case 2:
21049                                         this.viewDate = this.moveYear(this.viewDate, dir);
21050                                         break;
21051                         }
21052                         this.fill();
21053                         break;
21054                     case 'today':
21055                         var date = new Date();
21056                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21057 //                        this.fill()
21058                         this.setValue(this.formatDate(this.date));
21059                         
21060                         this.hidePopup();
21061                         break;
21062                 }
21063                 break;
21064             case 'span':
21065                 if (className.indexOf('disabled') < 0) {
21066                     this.viewDate.setUTCDate(1);
21067                     if (className.indexOf('month') > -1) {
21068                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21069                     } else {
21070                         var year = parseInt(html, 10) || 0;
21071                         this.viewDate.setUTCFullYear(year);
21072                         
21073                     }
21074                     
21075                     if(this.singleMode){
21076                         this.setValue(this.formatDate(this.viewDate));
21077                         this.hidePopup();
21078                         return;
21079                     }
21080                     
21081                     this.showMode(-1);
21082                     this.fill();
21083                 }
21084                 break;
21085                 
21086             case 'td':
21087                 //Roo.log(className);
21088                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21089                     var day = parseInt(html, 10) || 1;
21090                     var year = this.viewDate.getUTCFullYear(),
21091                         month = this.viewDate.getUTCMonth();
21092
21093                     if (className.indexOf('old') > -1) {
21094                         if(month === 0 ){
21095                             month = 11;
21096                             year -= 1;
21097                         }else{
21098                             month -= 1;
21099                         }
21100                     } else if (className.indexOf('new') > -1) {
21101                         if (month == 11) {
21102                             month = 0;
21103                             year += 1;
21104                         } else {
21105                             month += 1;
21106                         }
21107                     }
21108                     //Roo.log([year,month,day]);
21109                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21110                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21111 //                    this.fill();
21112                     //Roo.log(this.formatDate(this.date));
21113                     this.setValue(this.formatDate(this.date));
21114                     this.hidePopup();
21115                 }
21116                 break;
21117         }
21118     },
21119     
21120     setStartDate: function(startDate)
21121     {
21122         this.startDate = startDate || -Infinity;
21123         if (this.startDate !== -Infinity) {
21124             this.startDate = this.parseDate(this.startDate);
21125         }
21126         this.update();
21127         this.updateNavArrows();
21128     },
21129
21130     setEndDate: function(endDate)
21131     {
21132         this.endDate = endDate || Infinity;
21133         if (this.endDate !== Infinity) {
21134             this.endDate = this.parseDate(this.endDate);
21135         }
21136         this.update();
21137         this.updateNavArrows();
21138     },
21139     
21140     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21141     {
21142         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21143         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21144             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21145         }
21146         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21147             return parseInt(d, 10);
21148         });
21149         this.update();
21150         this.updateNavArrows();
21151     },
21152     
21153     updateNavArrows: function() 
21154     {
21155         if(this.singleMode){
21156             return;
21157         }
21158         
21159         var d = new Date(this.viewDate),
21160         year = d.getUTCFullYear(),
21161         month = d.getUTCMonth();
21162         
21163         Roo.each(this.picker().select('.prev', true).elements, function(v){
21164             v.show();
21165             switch (this.viewMode) {
21166                 case 0:
21167
21168                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21169                         v.hide();
21170                     }
21171                     break;
21172                 case 1:
21173                 case 2:
21174                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21175                         v.hide();
21176                     }
21177                     break;
21178             }
21179         });
21180         
21181         Roo.each(this.picker().select('.next', true).elements, function(v){
21182             v.show();
21183             switch (this.viewMode) {
21184                 case 0:
21185
21186                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21187                         v.hide();
21188                     }
21189                     break;
21190                 case 1:
21191                 case 2:
21192                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21193                         v.hide();
21194                     }
21195                     break;
21196             }
21197         })
21198     },
21199     
21200     moveMonth: function(date, dir)
21201     {
21202         if (!dir) {
21203             return date;
21204         }
21205         var new_date = new Date(date.valueOf()),
21206         day = new_date.getUTCDate(),
21207         month = new_date.getUTCMonth(),
21208         mag = Math.abs(dir),
21209         new_month, test;
21210         dir = dir > 0 ? 1 : -1;
21211         if (mag == 1){
21212             test = dir == -1
21213             // If going back one month, make sure month is not current month
21214             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21215             ? function(){
21216                 return new_date.getUTCMonth() == month;
21217             }
21218             // If going forward one month, make sure month is as expected
21219             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21220             : function(){
21221                 return new_date.getUTCMonth() != new_month;
21222             };
21223             new_month = month + dir;
21224             new_date.setUTCMonth(new_month);
21225             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21226             if (new_month < 0 || new_month > 11) {
21227                 new_month = (new_month + 12) % 12;
21228             }
21229         } else {
21230             // For magnitudes >1, move one month at a time...
21231             for (var i=0; i<mag; i++) {
21232                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21233                 new_date = this.moveMonth(new_date, dir);
21234             }
21235             // ...then reset the day, keeping it in the new month
21236             new_month = new_date.getUTCMonth();
21237             new_date.setUTCDate(day);
21238             test = function(){
21239                 return new_month != new_date.getUTCMonth();
21240             };
21241         }
21242         // Common date-resetting loop -- if date is beyond end of month, make it
21243         // end of month
21244         while (test()){
21245             new_date.setUTCDate(--day);
21246             new_date.setUTCMonth(new_month);
21247         }
21248         return new_date;
21249     },
21250
21251     moveYear: function(date, dir)
21252     {
21253         return this.moveMonth(date, dir*12);
21254     },
21255
21256     dateWithinRange: function(date)
21257     {
21258         return date >= this.startDate && date <= this.endDate;
21259     },
21260
21261     
21262     remove: function() 
21263     {
21264         this.picker().remove();
21265     },
21266     
21267     validateValue : function(value)
21268     {
21269         if(this.getVisibilityEl().hasClass('hidden')){
21270             return true;
21271         }
21272         
21273         if(value.length < 1)  {
21274             if(this.allowBlank){
21275                 return true;
21276             }
21277             return false;
21278         }
21279         
21280         if(value.length < this.minLength){
21281             return false;
21282         }
21283         if(value.length > this.maxLength){
21284             return false;
21285         }
21286         if(this.vtype){
21287             var vt = Roo.form.VTypes;
21288             if(!vt[this.vtype](value, this)){
21289                 return false;
21290             }
21291         }
21292         if(typeof this.validator == "function"){
21293             var msg = this.validator(value);
21294             if(msg !== true){
21295                 return false;
21296             }
21297         }
21298         
21299         if(this.regex && !this.regex.test(value)){
21300             return false;
21301         }
21302         
21303         if(typeof(this.parseDate(value)) == 'undefined'){
21304             return false;
21305         }
21306         
21307         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21308             return false;
21309         }      
21310         
21311         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21312             return false;
21313         } 
21314         
21315         
21316         return true;
21317     },
21318     
21319     reset : function()
21320     {
21321         this.date = this.viewDate = '';
21322         
21323         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21324     }
21325    
21326 });
21327
21328 Roo.apply(Roo.bootstrap.DateField,  {
21329     
21330     head : {
21331         tag: 'thead',
21332         cn: [
21333         {
21334             tag: 'tr',
21335             cn: [
21336             {
21337                 tag: 'th',
21338                 cls: 'prev',
21339                 html: '<i class="fa fa-arrow-left"/>'
21340             },
21341             {
21342                 tag: 'th',
21343                 cls: 'switch',
21344                 colspan: '5'
21345             },
21346             {
21347                 tag: 'th',
21348                 cls: 'next',
21349                 html: '<i class="fa fa-arrow-right"/>'
21350             }
21351
21352             ]
21353         }
21354         ]
21355     },
21356     
21357     content : {
21358         tag: 'tbody',
21359         cn: [
21360         {
21361             tag: 'tr',
21362             cn: [
21363             {
21364                 tag: 'td',
21365                 colspan: '7'
21366             }
21367             ]
21368         }
21369         ]
21370     },
21371     
21372     footer : {
21373         tag: 'tfoot',
21374         cn: [
21375         {
21376             tag: 'tr',
21377             cn: [
21378             {
21379                 tag: 'th',
21380                 colspan: '7',
21381                 cls: 'today'
21382             }
21383                     
21384             ]
21385         }
21386         ]
21387     },
21388     
21389     dates:{
21390         en: {
21391             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21392             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21393             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21394             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21395             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21396             today: "Today"
21397         }
21398     },
21399     
21400     modes: [
21401     {
21402         clsName: 'days',
21403         navFnc: 'Month',
21404         navStep: 1
21405     },
21406     {
21407         clsName: 'months',
21408         navFnc: 'FullYear',
21409         navStep: 1
21410     },
21411     {
21412         clsName: 'years',
21413         navFnc: 'FullYear',
21414         navStep: 10
21415     }]
21416 });
21417
21418 Roo.apply(Roo.bootstrap.DateField,  {
21419   
21420     template : {
21421         tag: 'div',
21422         cls: 'datepicker dropdown-menu roo-dynamic',
21423         cn: [
21424         {
21425             tag: 'div',
21426             cls: 'datepicker-days',
21427             cn: [
21428             {
21429                 tag: 'table',
21430                 cls: 'table-condensed',
21431                 cn:[
21432                 Roo.bootstrap.DateField.head,
21433                 {
21434                     tag: 'tbody'
21435                 },
21436                 Roo.bootstrap.DateField.footer
21437                 ]
21438             }
21439             ]
21440         },
21441         {
21442             tag: 'div',
21443             cls: 'datepicker-months',
21444             cn: [
21445             {
21446                 tag: 'table',
21447                 cls: 'table-condensed',
21448                 cn:[
21449                 Roo.bootstrap.DateField.head,
21450                 Roo.bootstrap.DateField.content,
21451                 Roo.bootstrap.DateField.footer
21452                 ]
21453             }
21454             ]
21455         },
21456         {
21457             tag: 'div',
21458             cls: 'datepicker-years',
21459             cn: [
21460             {
21461                 tag: 'table',
21462                 cls: 'table-condensed',
21463                 cn:[
21464                 Roo.bootstrap.DateField.head,
21465                 Roo.bootstrap.DateField.content,
21466                 Roo.bootstrap.DateField.footer
21467                 ]
21468             }
21469             ]
21470         }
21471         ]
21472     }
21473 });
21474
21475  
21476
21477  /*
21478  * - LGPL
21479  *
21480  * TimeField
21481  * 
21482  */
21483
21484 /**
21485  * @class Roo.bootstrap.TimeField
21486  * @extends Roo.bootstrap.Input
21487  * Bootstrap DateField class
21488  * 
21489  * 
21490  * @constructor
21491  * Create a new TimeField
21492  * @param {Object} config The config object
21493  */
21494
21495 Roo.bootstrap.TimeField = function(config){
21496     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21497     this.addEvents({
21498             /**
21499              * @event show
21500              * Fires when this field show.
21501              * @param {Roo.bootstrap.DateField} thisthis
21502              * @param {Mixed} date The date value
21503              */
21504             show : true,
21505             /**
21506              * @event show
21507              * Fires when this field hide.
21508              * @param {Roo.bootstrap.DateField} this
21509              * @param {Mixed} date The date value
21510              */
21511             hide : true,
21512             /**
21513              * @event select
21514              * Fires when select a date.
21515              * @param {Roo.bootstrap.DateField} this
21516              * @param {Mixed} date The date value
21517              */
21518             select : true
21519         });
21520 };
21521
21522 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21523     
21524     /**
21525      * @cfg {String} format
21526      * The default time format string which can be overriden for localization support.  The format must be
21527      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21528      */
21529     format : "H:i",
21530        
21531     onRender: function(ct, position)
21532     {
21533         
21534         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21535                 
21536         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21537         
21538         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21539         
21540         this.pop = this.picker().select('>.datepicker-time',true).first();
21541         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21542         
21543         this.picker().on('mousedown', this.onMousedown, this);
21544         this.picker().on('click', this.onClick, this);
21545         
21546         this.picker().addClass('datepicker-dropdown');
21547     
21548         this.fillTime();
21549         this.update();
21550             
21551         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21552         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21553         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21554         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21555         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21556         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21557
21558     },
21559     
21560     fireKey: function(e){
21561         if (!this.picker().isVisible()){
21562             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21563                 this.show();
21564             }
21565             return;
21566         }
21567
21568         e.preventDefault();
21569         
21570         switch(e.keyCode){
21571             case 27: // escape
21572                 this.hide();
21573                 break;
21574             case 37: // left
21575             case 39: // right
21576                 this.onTogglePeriod();
21577                 break;
21578             case 38: // up
21579                 this.onIncrementMinutes();
21580                 break;
21581             case 40: // down
21582                 this.onDecrementMinutes();
21583                 break;
21584             case 13: // enter
21585             case 9: // tab
21586                 this.setTime();
21587                 break;
21588         }
21589     },
21590     
21591     onClick: function(e) {
21592         e.stopPropagation();
21593         e.preventDefault();
21594     },
21595     
21596     picker : function()
21597     {
21598         return this.el.select('.datepicker', true).first();
21599     },
21600     
21601     fillTime: function()
21602     {    
21603         var time = this.pop.select('tbody', true).first();
21604         
21605         time.dom.innerHTML = '';
21606         
21607         time.createChild({
21608             tag: 'tr',
21609             cn: [
21610                 {
21611                     tag: 'td',
21612                     cn: [
21613                         {
21614                             tag: 'a',
21615                             href: '#',
21616                             cls: 'btn',
21617                             cn: [
21618                                 {
21619                                     tag: 'span',
21620                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
21621                                 }
21622                             ]
21623                         } 
21624                     ]
21625                 },
21626                 {
21627                     tag: 'td',
21628                     cls: 'separator'
21629                 },
21630                 {
21631                     tag: 'td',
21632                     cn: [
21633                         {
21634                             tag: 'a',
21635                             href: '#',
21636                             cls: 'btn',
21637                             cn: [
21638                                 {
21639                                     tag: 'span',
21640                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
21641                                 }
21642                             ]
21643                         }
21644                     ]
21645                 },
21646                 {
21647                     tag: 'td',
21648                     cls: 'separator'
21649                 }
21650             ]
21651         });
21652         
21653         time.createChild({
21654             tag: 'tr',
21655             cn: [
21656                 {
21657                     tag: 'td',
21658                     cn: [
21659                         {
21660                             tag: 'span',
21661                             cls: 'timepicker-hour',
21662                             html: '00'
21663                         }  
21664                     ]
21665                 },
21666                 {
21667                     tag: 'td',
21668                     cls: 'separator',
21669                     html: ':'
21670                 },
21671                 {
21672                     tag: 'td',
21673                     cn: [
21674                         {
21675                             tag: 'span',
21676                             cls: 'timepicker-minute',
21677                             html: '00'
21678                         }  
21679                     ]
21680                 },
21681                 {
21682                     tag: 'td',
21683                     cls: 'separator'
21684                 },
21685                 {
21686                     tag: 'td',
21687                     cn: [
21688                         {
21689                             tag: 'button',
21690                             type: 'button',
21691                             cls: 'btn btn-primary period',
21692                             html: 'AM'
21693                             
21694                         }
21695                     ]
21696                 }
21697             ]
21698         });
21699         
21700         time.createChild({
21701             tag: 'tr',
21702             cn: [
21703                 {
21704                     tag: 'td',
21705                     cn: [
21706                         {
21707                             tag: 'a',
21708                             href: '#',
21709                             cls: 'btn',
21710                             cn: [
21711                                 {
21712                                     tag: 'span',
21713                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
21714                                 }
21715                             ]
21716                         }
21717                     ]
21718                 },
21719                 {
21720                     tag: 'td',
21721                     cls: 'separator'
21722                 },
21723                 {
21724                     tag: 'td',
21725                     cn: [
21726                         {
21727                             tag: 'a',
21728                             href: '#',
21729                             cls: 'btn',
21730                             cn: [
21731                                 {
21732                                     tag: 'span',
21733                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
21734                                 }
21735                             ]
21736                         }
21737                     ]
21738                 },
21739                 {
21740                     tag: 'td',
21741                     cls: 'separator'
21742                 }
21743             ]
21744         });
21745         
21746     },
21747     
21748     update: function()
21749     {
21750         
21751         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21752         
21753         this.fill();
21754     },
21755     
21756     fill: function() 
21757     {
21758         var hours = this.time.getHours();
21759         var minutes = this.time.getMinutes();
21760         var period = 'AM';
21761         
21762         if(hours > 11){
21763             period = 'PM';
21764         }
21765         
21766         if(hours == 0){
21767             hours = 12;
21768         }
21769         
21770         
21771         if(hours > 12){
21772             hours = hours - 12;
21773         }
21774         
21775         if(hours < 10){
21776             hours = '0' + hours;
21777         }
21778         
21779         if(minutes < 10){
21780             minutes = '0' + minutes;
21781         }
21782         
21783         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21784         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21785         this.pop.select('button', true).first().dom.innerHTML = period;
21786         
21787     },
21788     
21789     place: function()
21790     {   
21791         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21792         
21793         var cls = ['bottom'];
21794         
21795         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21796             cls.pop();
21797             cls.push('top');
21798         }
21799         
21800         cls.push('right');
21801         
21802         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21803             cls.pop();
21804             cls.push('left');
21805         }
21806         
21807         this.picker().addClass(cls.join('-'));
21808         
21809         var _this = this;
21810         
21811         Roo.each(cls, function(c){
21812             if(c == 'bottom'){
21813                 _this.picker().setTop(_this.inputEl().getHeight());
21814                 return;
21815             }
21816             if(c == 'top'){
21817                 _this.picker().setTop(0 - _this.picker().getHeight());
21818                 return;
21819             }
21820             
21821             if(c == 'left'){
21822                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21823                 return;
21824             }
21825             if(c == 'right'){
21826                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21827                 return;
21828             }
21829         });
21830         
21831     },
21832   
21833     onFocus : function()
21834     {
21835         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21836         this.show();
21837     },
21838     
21839     onBlur : function()
21840     {
21841         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21842         this.hide();
21843     },
21844     
21845     show : function()
21846     {
21847         this.picker().show();
21848         this.pop.show();
21849         this.update();
21850         this.place();
21851         
21852         this.fireEvent('show', this, this.date);
21853     },
21854     
21855     hide : function()
21856     {
21857         this.picker().hide();
21858         this.pop.hide();
21859         
21860         this.fireEvent('hide', this, this.date);
21861     },
21862     
21863     setTime : function()
21864     {
21865         this.hide();
21866         this.setValue(this.time.format(this.format));
21867         
21868         this.fireEvent('select', this, this.date);
21869         
21870         
21871     },
21872     
21873     onMousedown: function(e){
21874         e.stopPropagation();
21875         e.preventDefault();
21876     },
21877     
21878     onIncrementHours: function()
21879     {
21880         Roo.log('onIncrementHours');
21881         this.time = this.time.add(Date.HOUR, 1);
21882         this.update();
21883         
21884     },
21885     
21886     onDecrementHours: function()
21887     {
21888         Roo.log('onDecrementHours');
21889         this.time = this.time.add(Date.HOUR, -1);
21890         this.update();
21891     },
21892     
21893     onIncrementMinutes: function()
21894     {
21895         Roo.log('onIncrementMinutes');
21896         this.time = this.time.add(Date.MINUTE, 1);
21897         this.update();
21898     },
21899     
21900     onDecrementMinutes: function()
21901     {
21902         Roo.log('onDecrementMinutes');
21903         this.time = this.time.add(Date.MINUTE, -1);
21904         this.update();
21905     },
21906     
21907     onTogglePeriod: function()
21908     {
21909         Roo.log('onTogglePeriod');
21910         this.time = this.time.add(Date.HOUR, 12);
21911         this.update();
21912     }
21913     
21914    
21915 });
21916
21917 Roo.apply(Roo.bootstrap.TimeField,  {
21918     
21919     content : {
21920         tag: 'tbody',
21921         cn: [
21922             {
21923                 tag: 'tr',
21924                 cn: [
21925                 {
21926                     tag: 'td',
21927                     colspan: '7'
21928                 }
21929                 ]
21930             }
21931         ]
21932     },
21933     
21934     footer : {
21935         tag: 'tfoot',
21936         cn: [
21937             {
21938                 tag: 'tr',
21939                 cn: [
21940                 {
21941                     tag: 'th',
21942                     colspan: '7',
21943                     cls: '',
21944                     cn: [
21945                         {
21946                             tag: 'button',
21947                             cls: 'btn btn-info ok',
21948                             html: 'OK'
21949                         }
21950                     ]
21951                 }
21952
21953                 ]
21954             }
21955         ]
21956     }
21957 });
21958
21959 Roo.apply(Roo.bootstrap.TimeField,  {
21960   
21961     template : {
21962         tag: 'div',
21963         cls: 'datepicker dropdown-menu',
21964         cn: [
21965             {
21966                 tag: 'div',
21967                 cls: 'datepicker-time',
21968                 cn: [
21969                 {
21970                     tag: 'table',
21971                     cls: 'table-condensed',
21972                     cn:[
21973                     Roo.bootstrap.TimeField.content,
21974                     Roo.bootstrap.TimeField.footer
21975                     ]
21976                 }
21977                 ]
21978             }
21979         ]
21980     }
21981 });
21982
21983  
21984
21985  /*
21986  * - LGPL
21987  *
21988  * MonthField
21989  * 
21990  */
21991
21992 /**
21993  * @class Roo.bootstrap.MonthField
21994  * @extends Roo.bootstrap.Input
21995  * Bootstrap MonthField class
21996  * 
21997  * @cfg {String} language default en
21998  * 
21999  * @constructor
22000  * Create a new MonthField
22001  * @param {Object} config The config object
22002  */
22003
22004 Roo.bootstrap.MonthField = function(config){
22005     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22006     
22007     this.addEvents({
22008         /**
22009          * @event show
22010          * Fires when this field show.
22011          * @param {Roo.bootstrap.MonthField} this
22012          * @param {Mixed} date The date value
22013          */
22014         show : true,
22015         /**
22016          * @event show
22017          * Fires when this field hide.
22018          * @param {Roo.bootstrap.MonthField} this
22019          * @param {Mixed} date The date value
22020          */
22021         hide : true,
22022         /**
22023          * @event select
22024          * Fires when select a date.
22025          * @param {Roo.bootstrap.MonthField} this
22026          * @param {String} oldvalue The old value
22027          * @param {String} newvalue The new value
22028          */
22029         select : true
22030     });
22031 };
22032
22033 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22034     
22035     onRender: function(ct, position)
22036     {
22037         
22038         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22039         
22040         this.language = this.language || 'en';
22041         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22042         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22043         
22044         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22045         this.isInline = false;
22046         this.isInput = true;
22047         this.component = this.el.select('.add-on', true).first() || false;
22048         this.component = (this.component && this.component.length === 0) ? false : this.component;
22049         this.hasInput = this.component && this.inputEL().length;
22050         
22051         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22052         
22053         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22054         
22055         this.picker().on('mousedown', this.onMousedown, this);
22056         this.picker().on('click', this.onClick, this);
22057         
22058         this.picker().addClass('datepicker-dropdown');
22059         
22060         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22061             v.setStyle('width', '189px');
22062         });
22063         
22064         this.fillMonths();
22065         
22066         this.update();
22067         
22068         if(this.isInline) {
22069             this.show();
22070         }
22071         
22072     },
22073     
22074     setValue: function(v, suppressEvent)
22075     {   
22076         var o = this.getValue();
22077         
22078         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22079         
22080         this.update();
22081
22082         if(suppressEvent !== true){
22083             this.fireEvent('select', this, o, v);
22084         }
22085         
22086     },
22087     
22088     getValue: function()
22089     {
22090         return this.value;
22091     },
22092     
22093     onClick: function(e) 
22094     {
22095         e.stopPropagation();
22096         e.preventDefault();
22097         
22098         var target = e.getTarget();
22099         
22100         if(target.nodeName.toLowerCase() === 'i'){
22101             target = Roo.get(target).dom.parentNode;
22102         }
22103         
22104         var nodeName = target.nodeName;
22105         var className = target.className;
22106         var html = target.innerHTML;
22107         
22108         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22109             return;
22110         }
22111         
22112         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22113         
22114         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22115         
22116         this.hide();
22117                         
22118     },
22119     
22120     picker : function()
22121     {
22122         return this.pickerEl;
22123     },
22124     
22125     fillMonths: function()
22126     {    
22127         var i = 0;
22128         var months = this.picker().select('>.datepicker-months td', true).first();
22129         
22130         months.dom.innerHTML = '';
22131         
22132         while (i < 12) {
22133             var month = {
22134                 tag: 'span',
22135                 cls: 'month',
22136                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22137             };
22138             
22139             months.createChild(month);
22140         }
22141         
22142     },
22143     
22144     update: function()
22145     {
22146         var _this = this;
22147         
22148         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22149             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22150         }
22151         
22152         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22153             e.removeClass('active');
22154             
22155             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22156                 e.addClass('active');
22157             }
22158         })
22159     },
22160     
22161     place: function()
22162     {
22163         if(this.isInline) {
22164             return;
22165         }
22166         
22167         this.picker().removeClass(['bottom', 'top']);
22168         
22169         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22170             /*
22171              * place to the top of element!
22172              *
22173              */
22174             
22175             this.picker().addClass('top');
22176             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22177             
22178             return;
22179         }
22180         
22181         this.picker().addClass('bottom');
22182         
22183         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22184     },
22185     
22186     onFocus : function()
22187     {
22188         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22189         this.show();
22190     },
22191     
22192     onBlur : function()
22193     {
22194         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22195         
22196         var d = this.inputEl().getValue();
22197         
22198         this.setValue(d);
22199                 
22200         this.hide();
22201     },
22202     
22203     show : function()
22204     {
22205         this.picker().show();
22206         this.picker().select('>.datepicker-months', true).first().show();
22207         this.update();
22208         this.place();
22209         
22210         this.fireEvent('show', this, this.date);
22211     },
22212     
22213     hide : function()
22214     {
22215         if(this.isInline) {
22216             return;
22217         }
22218         this.picker().hide();
22219         this.fireEvent('hide', this, this.date);
22220         
22221     },
22222     
22223     onMousedown: function(e)
22224     {
22225         e.stopPropagation();
22226         e.preventDefault();
22227     },
22228     
22229     keyup: function(e)
22230     {
22231         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22232         this.update();
22233     },
22234
22235     fireKey: function(e)
22236     {
22237         if (!this.picker().isVisible()){
22238             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22239                 this.show();
22240             }
22241             return;
22242         }
22243         
22244         var dir;
22245         
22246         switch(e.keyCode){
22247             case 27: // escape
22248                 this.hide();
22249                 e.preventDefault();
22250                 break;
22251             case 37: // left
22252             case 39: // right
22253                 dir = e.keyCode == 37 ? -1 : 1;
22254                 
22255                 this.vIndex = this.vIndex + dir;
22256                 
22257                 if(this.vIndex < 0){
22258                     this.vIndex = 0;
22259                 }
22260                 
22261                 if(this.vIndex > 11){
22262                     this.vIndex = 11;
22263                 }
22264                 
22265                 if(isNaN(this.vIndex)){
22266                     this.vIndex = 0;
22267                 }
22268                 
22269                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22270                 
22271                 break;
22272             case 38: // up
22273             case 40: // down
22274                 
22275                 dir = e.keyCode == 38 ? -1 : 1;
22276                 
22277                 this.vIndex = this.vIndex + dir * 4;
22278                 
22279                 if(this.vIndex < 0){
22280                     this.vIndex = 0;
22281                 }
22282                 
22283                 if(this.vIndex > 11){
22284                     this.vIndex = 11;
22285                 }
22286                 
22287                 if(isNaN(this.vIndex)){
22288                     this.vIndex = 0;
22289                 }
22290                 
22291                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22292                 break;
22293                 
22294             case 13: // enter
22295                 
22296                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22297                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22298                 }
22299                 
22300                 this.hide();
22301                 e.preventDefault();
22302                 break;
22303             case 9: // tab
22304                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22305                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22306                 }
22307                 this.hide();
22308                 break;
22309             case 16: // shift
22310             case 17: // ctrl
22311             case 18: // alt
22312                 break;
22313             default :
22314                 this.hide();
22315                 
22316         }
22317     },
22318     
22319     remove: function() 
22320     {
22321         this.picker().remove();
22322     }
22323    
22324 });
22325
22326 Roo.apply(Roo.bootstrap.MonthField,  {
22327     
22328     content : {
22329         tag: 'tbody',
22330         cn: [
22331         {
22332             tag: 'tr',
22333             cn: [
22334             {
22335                 tag: 'td',
22336                 colspan: '7'
22337             }
22338             ]
22339         }
22340         ]
22341     },
22342     
22343     dates:{
22344         en: {
22345             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22346             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22347         }
22348     }
22349 });
22350
22351 Roo.apply(Roo.bootstrap.MonthField,  {
22352   
22353     template : {
22354         tag: 'div',
22355         cls: 'datepicker dropdown-menu roo-dynamic',
22356         cn: [
22357             {
22358                 tag: 'div',
22359                 cls: 'datepicker-months',
22360                 cn: [
22361                 {
22362                     tag: 'table',
22363                     cls: 'table-condensed',
22364                     cn:[
22365                         Roo.bootstrap.DateField.content
22366                     ]
22367                 }
22368                 ]
22369             }
22370         ]
22371     }
22372 });
22373
22374  
22375
22376  
22377  /*
22378  * - LGPL
22379  *
22380  * CheckBox
22381  * 
22382  */
22383
22384 /**
22385  * @class Roo.bootstrap.CheckBox
22386  * @extends Roo.bootstrap.Input
22387  * Bootstrap CheckBox class
22388  * 
22389  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22390  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22391  * @cfg {String} boxLabel The text that appears beside the checkbox
22392  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22393  * @cfg {Boolean} checked initnal the element
22394  * @cfg {Boolean} inline inline the element (default false)
22395  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22396  * @cfg {String} tooltip label tooltip
22397  * 
22398  * @constructor
22399  * Create a new CheckBox
22400  * @param {Object} config The config object
22401  */
22402
22403 Roo.bootstrap.CheckBox = function(config){
22404     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22405    
22406     this.addEvents({
22407         /**
22408         * @event check
22409         * Fires when the element is checked or unchecked.
22410         * @param {Roo.bootstrap.CheckBox} this This input
22411         * @param {Boolean} checked The new checked value
22412         */
22413        check : true,
22414        /**
22415         * @event click
22416         * Fires when the element is click.
22417         * @param {Roo.bootstrap.CheckBox} this This input
22418         */
22419        click : true
22420     });
22421     
22422 };
22423
22424 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22425   
22426     inputType: 'checkbox',
22427     inputValue: 1,
22428     valueOff: 0,
22429     boxLabel: false,
22430     checked: false,
22431     weight : false,
22432     inline: false,
22433     tooltip : '',
22434     
22435     // checkbox success does not make any sense really.. 
22436     invalidClass : "",
22437     validClass : "",
22438     
22439     
22440     getAutoCreate : function()
22441     {
22442         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22443         
22444         var id = Roo.id();
22445         
22446         var cfg = {};
22447         
22448         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22449         
22450         if(this.inline){
22451             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22452         }
22453         
22454         var input =  {
22455             tag: 'input',
22456             id : id,
22457             type : this.inputType,
22458             value : this.inputValue,
22459             cls : 'roo-' + this.inputType, //'form-box',
22460             placeholder : this.placeholder || ''
22461             
22462         };
22463         
22464         if(this.inputType != 'radio'){
22465             var hidden =  {
22466                 tag: 'input',
22467                 type : 'hidden',
22468                 cls : 'roo-hidden-value',
22469                 value : this.checked ? this.inputValue : this.valueOff
22470             };
22471         }
22472         
22473             
22474         if (this.weight) { // Validity check?
22475             cfg.cls += " " + this.inputType + "-" + this.weight;
22476         }
22477         
22478         if (this.disabled) {
22479             input.disabled=true;
22480         }
22481         
22482         if(this.checked){
22483             input.checked = this.checked;
22484         }
22485         
22486         if (this.name) {
22487             
22488             input.name = this.name;
22489             
22490             if(this.inputType != 'radio'){
22491                 hidden.name = this.name;
22492                 input.name = '_hidden_' + this.name;
22493             }
22494         }
22495         
22496         if (this.size) {
22497             input.cls += ' input-' + this.size;
22498         }
22499         
22500         var settings=this;
22501         
22502         ['xs','sm','md','lg'].map(function(size){
22503             if (settings[size]) {
22504                 cfg.cls += ' col-' + size + '-' + settings[size];
22505             }
22506         });
22507         
22508         var inputblock = input;
22509          
22510         if (this.before || this.after) {
22511             
22512             inputblock = {
22513                 cls : 'input-group',
22514                 cn :  [] 
22515             };
22516             
22517             if (this.before) {
22518                 inputblock.cn.push({
22519                     tag :'span',
22520                     cls : 'input-group-addon',
22521                     html : this.before
22522                 });
22523             }
22524             
22525             inputblock.cn.push(input);
22526             
22527             if(this.inputType != 'radio'){
22528                 inputblock.cn.push(hidden);
22529             }
22530             
22531             if (this.after) {
22532                 inputblock.cn.push({
22533                     tag :'span',
22534                     cls : 'input-group-addon',
22535                     html : this.after
22536                 });
22537             }
22538             
22539         }
22540         var boxLabelCfg = false;
22541         
22542         if(this.boxLabel){
22543            
22544             boxLabelCfg = {
22545                 tag: 'label',
22546                 //'for': id, // box label is handled by onclick - so no for...
22547                 cls: 'box-label',
22548                 html: this.boxLabel
22549             };
22550             if(this.tooltip){
22551                 boxLabelCfg.tooltip = this.tooltip;
22552             }
22553              
22554         }
22555         
22556         
22557         if (align ==='left' && this.fieldLabel.length) {
22558 //                Roo.log("left and has label");
22559             cfg.cn = [
22560                 {
22561                     tag: 'label',
22562                     'for' :  id,
22563                     cls : 'control-label',
22564                     html : this.fieldLabel
22565                 },
22566                 {
22567                     cls : "", 
22568                     cn: [
22569                         inputblock
22570                     ]
22571                 }
22572             ];
22573             
22574             if (boxLabelCfg) {
22575                 cfg.cn[1].cn.push(boxLabelCfg);
22576             }
22577             
22578             if(this.labelWidth > 12){
22579                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22580             }
22581             
22582             if(this.labelWidth < 13 && this.labelmd == 0){
22583                 this.labelmd = this.labelWidth;
22584             }
22585             
22586             if(this.labellg > 0){
22587                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22588                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22589             }
22590             
22591             if(this.labelmd > 0){
22592                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22593                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22594             }
22595             
22596             if(this.labelsm > 0){
22597                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22598                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22599             }
22600             
22601             if(this.labelxs > 0){
22602                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22603                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22604             }
22605             
22606         } else if ( this.fieldLabel.length) {
22607 //                Roo.log(" label");
22608                 cfg.cn = [
22609                    
22610                     {
22611                         tag: this.boxLabel ? 'span' : 'label',
22612                         'for': id,
22613                         cls: 'control-label box-input-label',
22614                         //cls : 'input-group-addon',
22615                         html : this.fieldLabel
22616                     },
22617                     
22618                     inputblock
22619                     
22620                 ];
22621                 if (boxLabelCfg) {
22622                     cfg.cn.push(boxLabelCfg);
22623                 }
22624
22625         } else {
22626             
22627 //                Roo.log(" no label && no align");
22628                 cfg.cn = [  inputblock ] ;
22629                 if (boxLabelCfg) {
22630                     cfg.cn.push(boxLabelCfg);
22631                 }
22632
22633                 
22634         }
22635         
22636        
22637         
22638         if(this.inputType != 'radio'){
22639             cfg.cn.push(hidden);
22640         }
22641         
22642         return cfg;
22643         
22644     },
22645     
22646     /**
22647      * return the real input element.
22648      */
22649     inputEl: function ()
22650     {
22651         return this.el.select('input.roo-' + this.inputType,true).first();
22652     },
22653     hiddenEl: function ()
22654     {
22655         return this.el.select('input.roo-hidden-value',true).first();
22656     },
22657     
22658     labelEl: function()
22659     {
22660         return this.el.select('label.control-label',true).first();
22661     },
22662     /* depricated... */
22663     
22664     label: function()
22665     {
22666         return this.labelEl();
22667     },
22668     
22669     boxLabelEl: function()
22670     {
22671         return this.el.select('label.box-label',true).first();
22672     },
22673     
22674     initEvents : function()
22675     {
22676 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22677         
22678         this.inputEl().on('click', this.onClick,  this);
22679         
22680         if (this.boxLabel) { 
22681             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
22682         }
22683         
22684         this.startValue = this.getValue();
22685         
22686         if(this.groupId){
22687             Roo.bootstrap.CheckBox.register(this);
22688         }
22689     },
22690     
22691     onClick : function(e)
22692     {   
22693         if(this.fireEvent('click', this, e) !== false){
22694             this.setChecked(!this.checked);
22695         }
22696         
22697     },
22698     
22699     setChecked : function(state,suppressEvent)
22700     {
22701         this.startValue = this.getValue();
22702
22703         if(this.inputType == 'radio'){
22704             
22705             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22706                 e.dom.checked = false;
22707             });
22708             
22709             this.inputEl().dom.checked = true;
22710             
22711             this.inputEl().dom.value = this.inputValue;
22712             
22713             if(suppressEvent !== true){
22714                 this.fireEvent('check', this, true);
22715             }
22716             
22717             this.validate();
22718             
22719             return;
22720         }
22721         
22722         this.checked = state;
22723         
22724         this.inputEl().dom.checked = state;
22725         
22726         
22727         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22728         
22729         if(suppressEvent !== true){
22730             this.fireEvent('check', this, state);
22731         }
22732         
22733         this.validate();
22734     },
22735     
22736     getValue : function()
22737     {
22738         if(this.inputType == 'radio'){
22739             return this.getGroupValue();
22740         }
22741         
22742         return this.hiddenEl().dom.value;
22743         
22744     },
22745     
22746     getGroupValue : function()
22747     {
22748         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22749             return '';
22750         }
22751         
22752         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22753     },
22754     
22755     setValue : function(v,suppressEvent)
22756     {
22757         if(this.inputType == 'radio'){
22758             this.setGroupValue(v, suppressEvent);
22759             return;
22760         }
22761         
22762         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22763         
22764         this.validate();
22765     },
22766     
22767     setGroupValue : function(v, suppressEvent)
22768     {
22769         this.startValue = this.getValue();
22770         
22771         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22772             e.dom.checked = false;
22773             
22774             if(e.dom.value == v){
22775                 e.dom.checked = true;
22776             }
22777         });
22778         
22779         if(suppressEvent !== true){
22780             this.fireEvent('check', this, true);
22781         }
22782
22783         this.validate();
22784         
22785         return;
22786     },
22787     
22788     validate : function()
22789     {
22790         if(this.getVisibilityEl().hasClass('hidden')){
22791             return true;
22792         }
22793         
22794         if(
22795                 this.disabled || 
22796                 (this.inputType == 'radio' && this.validateRadio()) ||
22797                 (this.inputType == 'checkbox' && this.validateCheckbox())
22798         ){
22799             this.markValid();
22800             return true;
22801         }
22802         
22803         this.markInvalid();
22804         return false;
22805     },
22806     
22807     validateRadio : function()
22808     {
22809         if(this.getVisibilityEl().hasClass('hidden')){
22810             return true;
22811         }
22812         
22813         if(this.allowBlank){
22814             return true;
22815         }
22816         
22817         var valid = false;
22818         
22819         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22820             if(!e.dom.checked){
22821                 return;
22822             }
22823             
22824             valid = true;
22825             
22826             return false;
22827         });
22828         
22829         return valid;
22830     },
22831     
22832     validateCheckbox : function()
22833     {
22834         if(!this.groupId){
22835             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22836             //return (this.getValue() == this.inputValue) ? true : false;
22837         }
22838         
22839         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22840         
22841         if(!group){
22842             return false;
22843         }
22844         
22845         var r = false;
22846         
22847         for(var i in group){
22848             if(group[i].el.isVisible(true)){
22849                 r = false;
22850                 break;
22851             }
22852             
22853             r = true;
22854         }
22855         
22856         for(var i in group){
22857             if(r){
22858                 break;
22859             }
22860             
22861             r = (group[i].getValue() == group[i].inputValue) ? true : false;
22862         }
22863         
22864         return r;
22865     },
22866     
22867     /**
22868      * Mark this field as valid
22869      */
22870     markValid : function()
22871     {
22872         var _this = this;
22873         
22874         this.fireEvent('valid', this);
22875         
22876         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22877         
22878         if(this.groupId){
22879             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22880         }
22881         
22882         if(label){
22883             label.markValid();
22884         }
22885
22886         if(this.inputType == 'radio'){
22887             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22888                 var fg = e.findParent('.form-group', false, true);
22889                 if (Roo.bootstrap.version == 3) {
22890                     fg.removeClass([_this.invalidClass, _this.validClass]);
22891                     fg.addClass(_this.validClass);
22892                 } else {
22893                     fg.removeClass(['is-valid', 'is-invalid']);
22894                     fg.addClass('is-valid');
22895                 }
22896             });
22897             
22898             return;
22899         }
22900
22901         if(!this.groupId){
22902             var fg = this.el.findParent('.form-group', false, true);
22903             if (Roo.bootstrap.version == 3) {
22904                 fg.removeClass([this.invalidClass, this.validClass]);
22905                 fg.addClass(this.validClass);
22906             } else {
22907                 fg.removeClass(['is-valid', 'is-invalid']);
22908                 fg.addClass('is-valid');
22909             }
22910             return;
22911         }
22912         
22913         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22914         
22915         if(!group){
22916             return;
22917         }
22918         
22919         for(var i in group){
22920             var fg = group[i].el.findParent('.form-group', false, true);
22921             if (Roo.bootstrap.version == 3) {
22922                 fg.removeClass([this.invalidClass, this.validClass]);
22923                 fg.addClass(this.validClass);
22924             } else {
22925                 fg.removeClass(['is-valid', 'is-invalid']);
22926                 fg.addClass('is-valid');
22927             }
22928         }
22929     },
22930     
22931      /**
22932      * Mark this field as invalid
22933      * @param {String} msg The validation message
22934      */
22935     markInvalid : function(msg)
22936     {
22937         if(this.allowBlank){
22938             return;
22939         }
22940         
22941         var _this = this;
22942         
22943         this.fireEvent('invalid', this, msg);
22944         
22945         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22946         
22947         if(this.groupId){
22948             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22949         }
22950         
22951         if(label){
22952             label.markInvalid();
22953         }
22954             
22955         if(this.inputType == 'radio'){
22956             
22957             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22958                 var fg = e.findParent('.form-group', false, true);
22959                 if (Roo.bootstrap.version == 3) {
22960                     fg.removeClass([_this.invalidClass, _this.validClass]);
22961                     fg.addClass(_this.invalidClass);
22962                 } else {
22963                     fg.removeClass(['is-invalid', 'is-valid']);
22964                     fg.addClass('is-invalid');
22965                 }
22966             });
22967             
22968             return;
22969         }
22970         
22971         if(!this.groupId){
22972             var fg = this.el.findParent('.form-group', false, true);
22973             if (Roo.bootstrap.version == 3) {
22974                 fg.removeClass([_this.invalidClass, _this.validClass]);
22975                 fg.addClass(_this.invalidClass);
22976             } else {
22977                 fg.removeClass(['is-invalid', 'is-valid']);
22978                 fg.addClass('is-invalid');
22979             }
22980             return;
22981         }
22982         
22983         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22984         
22985         if(!group){
22986             return;
22987         }
22988         
22989         for(var i in group){
22990             var fg = group[i].el.findParent('.form-group', false, true);
22991             if (Roo.bootstrap.version == 3) {
22992                 fg.removeClass([_this.invalidClass, _this.validClass]);
22993                 fg.addClass(_this.invalidClass);
22994             } else {
22995                 fg.removeClass(['is-invalid', 'is-valid']);
22996                 fg.addClass('is-invalid');
22997             }
22998         }
22999         
23000     },
23001     
23002     clearInvalid : function()
23003     {
23004         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23005         
23006         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23007         
23008         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23009         
23010         if (label && label.iconEl) {
23011             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23012             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23013         }
23014     },
23015     
23016     disable : function()
23017     {
23018         if(this.inputType != 'radio'){
23019             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23020             return;
23021         }
23022         
23023         var _this = this;
23024         
23025         if(this.rendered){
23026             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23027                 _this.getActionEl().addClass(this.disabledClass);
23028                 e.dom.disabled = true;
23029             });
23030         }
23031         
23032         this.disabled = true;
23033         this.fireEvent("disable", this);
23034         return this;
23035     },
23036
23037     enable : function()
23038     {
23039         if(this.inputType != 'radio'){
23040             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23041             return;
23042         }
23043         
23044         var _this = this;
23045         
23046         if(this.rendered){
23047             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23048                 _this.getActionEl().removeClass(this.disabledClass);
23049                 e.dom.disabled = false;
23050             });
23051         }
23052         
23053         this.disabled = false;
23054         this.fireEvent("enable", this);
23055         return this;
23056     },
23057     
23058     setBoxLabel : function(v)
23059     {
23060         this.boxLabel = v;
23061         
23062         if(this.rendered){
23063             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23064         }
23065     }
23066
23067 });
23068
23069 Roo.apply(Roo.bootstrap.CheckBox, {
23070     
23071     groups: {},
23072     
23073      /**
23074     * register a CheckBox Group
23075     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23076     */
23077     register : function(checkbox)
23078     {
23079         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23080             this.groups[checkbox.groupId] = {};
23081         }
23082         
23083         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23084             return;
23085         }
23086         
23087         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23088         
23089     },
23090     /**
23091     * fetch a CheckBox Group based on the group ID
23092     * @param {string} the group ID
23093     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23094     */
23095     get: function(groupId) {
23096         if (typeof(this.groups[groupId]) == 'undefined') {
23097             return false;
23098         }
23099         
23100         return this.groups[groupId] ;
23101     }
23102     
23103     
23104 });
23105 /*
23106  * - LGPL
23107  *
23108  * RadioItem
23109  * 
23110  */
23111
23112 /**
23113  * @class Roo.bootstrap.Radio
23114  * @extends Roo.bootstrap.Component
23115  * Bootstrap Radio class
23116  * @cfg {String} boxLabel - the label associated
23117  * @cfg {String} value - the value of radio
23118  * 
23119  * @constructor
23120  * Create a new Radio
23121  * @param {Object} config The config object
23122  */
23123 Roo.bootstrap.Radio = function(config){
23124     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23125     
23126 };
23127
23128 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23129     
23130     boxLabel : '',
23131     
23132     value : '',
23133     
23134     getAutoCreate : function()
23135     {
23136         var cfg = {
23137             tag : 'div',
23138             cls : 'form-group radio',
23139             cn : [
23140                 {
23141                     tag : 'label',
23142                     cls : 'box-label',
23143                     html : this.boxLabel
23144                 }
23145             ]
23146         };
23147         
23148         return cfg;
23149     },
23150     
23151     initEvents : function() 
23152     {
23153         this.parent().register(this);
23154         
23155         this.el.on('click', this.onClick, this);
23156         
23157     },
23158     
23159     onClick : function(e)
23160     {
23161         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23162             this.setChecked(true);
23163         }
23164     },
23165     
23166     setChecked : function(state, suppressEvent)
23167     {
23168         this.parent().setValue(this.value, suppressEvent);
23169         
23170     },
23171     
23172     setBoxLabel : function(v)
23173     {
23174         this.boxLabel = v;
23175         
23176         if(this.rendered){
23177             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23178         }
23179     }
23180     
23181 });
23182  
23183
23184  /*
23185  * - LGPL
23186  *
23187  * Input
23188  * 
23189  */
23190
23191 /**
23192  * @class Roo.bootstrap.SecurePass
23193  * @extends Roo.bootstrap.Input
23194  * Bootstrap SecurePass class
23195  *
23196  * 
23197  * @constructor
23198  * Create a new SecurePass
23199  * @param {Object} config The config object
23200  */
23201  
23202 Roo.bootstrap.SecurePass = function (config) {
23203     // these go here, so the translation tool can replace them..
23204     this.errors = {
23205         PwdEmpty: "Please type a password, and then retype it to confirm.",
23206         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23207         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23208         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23209         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23210         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23211         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23212         TooWeak: "Your password is Too Weak."
23213     },
23214     this.meterLabel = "Password strength:";
23215     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23216     this.meterClass = [
23217         "roo-password-meter-tooweak", 
23218         "roo-password-meter-weak", 
23219         "roo-password-meter-medium", 
23220         "roo-password-meter-strong", 
23221         "roo-password-meter-grey"
23222     ];
23223     
23224     this.errors = {};
23225     
23226     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23227 }
23228
23229 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23230     /**
23231      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23232      * {
23233      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23234      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23235      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23236      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23237      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23238      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23239      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23240      * })
23241      */
23242     // private
23243     
23244     meterWidth: 300,
23245     errorMsg :'',    
23246     errors: false,
23247     imageRoot: '/',
23248     /**
23249      * @cfg {String/Object} Label for the strength meter (defaults to
23250      * 'Password strength:')
23251      */
23252     // private
23253     meterLabel: '',
23254     /**
23255      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23256      * ['Weak', 'Medium', 'Strong'])
23257      */
23258     // private    
23259     pwdStrengths: false,    
23260     // private
23261     strength: 0,
23262     // private
23263     _lastPwd: null,
23264     // private
23265     kCapitalLetter: 0,
23266     kSmallLetter: 1,
23267     kDigit: 2,
23268     kPunctuation: 3,
23269     
23270     insecure: false,
23271     // private
23272     initEvents: function ()
23273     {
23274         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23275
23276         if (this.el.is('input[type=password]') && Roo.isSafari) {
23277             this.el.on('keydown', this.SafariOnKeyDown, this);
23278         }
23279
23280         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23281     },
23282     // private
23283     onRender: function (ct, position)
23284     {
23285         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23286         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23287         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23288
23289         this.trigger.createChild({
23290                    cn: [
23291                     {
23292                     //id: 'PwdMeter',
23293                     tag: 'div',
23294                     cls: 'roo-password-meter-grey col-xs-12',
23295                     style: {
23296                         //width: 0,
23297                         //width: this.meterWidth + 'px'                                                
23298                         }
23299                     },
23300                     {                            
23301                          cls: 'roo-password-meter-text'                          
23302                     }
23303                 ]            
23304         });
23305
23306          
23307         if (this.hideTrigger) {
23308             this.trigger.setDisplayed(false);
23309         }
23310         this.setSize(this.width || '', this.height || '');
23311     },
23312     // private
23313     onDestroy: function ()
23314     {
23315         if (this.trigger) {
23316             this.trigger.removeAllListeners();
23317             this.trigger.remove();
23318         }
23319         if (this.wrap) {
23320             this.wrap.remove();
23321         }
23322         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23323     },
23324     // private
23325     checkStrength: function ()
23326     {
23327         var pwd = this.inputEl().getValue();
23328         if (pwd == this._lastPwd) {
23329             return;
23330         }
23331
23332         var strength;
23333         if (this.ClientSideStrongPassword(pwd)) {
23334             strength = 3;
23335         } else if (this.ClientSideMediumPassword(pwd)) {
23336             strength = 2;
23337         } else if (this.ClientSideWeakPassword(pwd)) {
23338             strength = 1;
23339         } else {
23340             strength = 0;
23341         }
23342         
23343         Roo.log('strength1: ' + strength);
23344         
23345         //var pm = this.trigger.child('div/div/div').dom;
23346         var pm = this.trigger.child('div/div');
23347         pm.removeClass(this.meterClass);
23348         pm.addClass(this.meterClass[strength]);
23349                 
23350         
23351         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23352                 
23353         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23354         
23355         this._lastPwd = pwd;
23356     },
23357     reset: function ()
23358     {
23359         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23360         
23361         this._lastPwd = '';
23362         
23363         var pm = this.trigger.child('div/div');
23364         pm.removeClass(this.meterClass);
23365         pm.addClass('roo-password-meter-grey');        
23366         
23367         
23368         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23369         
23370         pt.innerHTML = '';
23371         this.inputEl().dom.type='password';
23372     },
23373     // private
23374     validateValue: function (value)
23375     {
23376         
23377         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23378             return false;
23379         }
23380         if (value.length == 0) {
23381             if (this.allowBlank) {
23382                 this.clearInvalid();
23383                 return true;
23384             }
23385
23386             this.markInvalid(this.errors.PwdEmpty);
23387             this.errorMsg = this.errors.PwdEmpty;
23388             return false;
23389         }
23390         
23391         if(this.insecure){
23392             return true;
23393         }
23394         
23395         if ('[\x21-\x7e]*'.match(value)) {
23396             this.markInvalid(this.errors.PwdBadChar);
23397             this.errorMsg = this.errors.PwdBadChar;
23398             return false;
23399         }
23400         if (value.length < 6) {
23401             this.markInvalid(this.errors.PwdShort);
23402             this.errorMsg = this.errors.PwdShort;
23403             return false;
23404         }
23405         if (value.length > 16) {
23406             this.markInvalid(this.errors.PwdLong);
23407             this.errorMsg = this.errors.PwdLong;
23408             return false;
23409         }
23410         var strength;
23411         if (this.ClientSideStrongPassword(value)) {
23412             strength = 3;
23413         } else if (this.ClientSideMediumPassword(value)) {
23414             strength = 2;
23415         } else if (this.ClientSideWeakPassword(value)) {
23416             strength = 1;
23417         } else {
23418             strength = 0;
23419         }
23420
23421         
23422         if (strength < 2) {
23423             //this.markInvalid(this.errors.TooWeak);
23424             this.errorMsg = this.errors.TooWeak;
23425             //return false;
23426         }
23427         
23428         
23429         console.log('strength2: ' + strength);
23430         
23431         //var pm = this.trigger.child('div/div/div').dom;
23432         
23433         var pm = this.trigger.child('div/div');
23434         pm.removeClass(this.meterClass);
23435         pm.addClass(this.meterClass[strength]);
23436                 
23437         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23438                 
23439         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23440         
23441         this.errorMsg = ''; 
23442         return true;
23443     },
23444     // private
23445     CharacterSetChecks: function (type)
23446     {
23447         this.type = type;
23448         this.fResult = false;
23449     },
23450     // private
23451     isctype: function (character, type)
23452     {
23453         switch (type) {  
23454             case this.kCapitalLetter:
23455                 if (character >= 'A' && character <= 'Z') {
23456                     return true;
23457                 }
23458                 break;
23459             
23460             case this.kSmallLetter:
23461                 if (character >= 'a' && character <= 'z') {
23462                     return true;
23463                 }
23464                 break;
23465             
23466             case this.kDigit:
23467                 if (character >= '0' && character <= '9') {
23468                     return true;
23469                 }
23470                 break;
23471             
23472             case this.kPunctuation:
23473                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23474                     return true;
23475                 }
23476                 break;
23477             
23478             default:
23479                 return false;
23480         }
23481
23482     },
23483     // private
23484     IsLongEnough: function (pwd, size)
23485     {
23486         return !(pwd == null || isNaN(size) || pwd.length < size);
23487     },
23488     // private
23489     SpansEnoughCharacterSets: function (word, nb)
23490     {
23491         if (!this.IsLongEnough(word, nb))
23492         {
23493             return false;
23494         }
23495
23496         var characterSetChecks = new Array(
23497             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23498             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23499         );
23500         
23501         for (var index = 0; index < word.length; ++index) {
23502             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23503                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23504                     characterSetChecks[nCharSet].fResult = true;
23505                     break;
23506                 }
23507             }
23508         }
23509
23510         var nCharSets = 0;
23511         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23512             if (characterSetChecks[nCharSet].fResult) {
23513                 ++nCharSets;
23514             }
23515         }
23516
23517         if (nCharSets < nb) {
23518             return false;
23519         }
23520         return true;
23521     },
23522     // private
23523     ClientSideStrongPassword: function (pwd)
23524     {
23525         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23526     },
23527     // private
23528     ClientSideMediumPassword: function (pwd)
23529     {
23530         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23531     },
23532     // private
23533     ClientSideWeakPassword: function (pwd)
23534     {
23535         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23536     }
23537           
23538 })//<script type="text/javascript">
23539
23540 /*
23541  * Based  Ext JS Library 1.1.1
23542  * Copyright(c) 2006-2007, Ext JS, LLC.
23543  * LGPL
23544  *
23545  */
23546  
23547 /**
23548  * @class Roo.HtmlEditorCore
23549  * @extends Roo.Component
23550  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23551  *
23552  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23553  */
23554
23555 Roo.HtmlEditorCore = function(config){
23556     
23557     
23558     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23559     
23560     
23561     this.addEvents({
23562         /**
23563          * @event initialize
23564          * Fires when the editor is fully initialized (including the iframe)
23565          * @param {Roo.HtmlEditorCore} this
23566          */
23567         initialize: true,
23568         /**
23569          * @event activate
23570          * Fires when the editor is first receives the focus. Any insertion must wait
23571          * until after this event.
23572          * @param {Roo.HtmlEditorCore} this
23573          */
23574         activate: true,
23575          /**
23576          * @event beforesync
23577          * Fires before the textarea is updated with content from the editor iframe. Return false
23578          * to cancel the sync.
23579          * @param {Roo.HtmlEditorCore} this
23580          * @param {String} html
23581          */
23582         beforesync: true,
23583          /**
23584          * @event beforepush
23585          * Fires before the iframe editor is updated with content from the textarea. Return false
23586          * to cancel the push.
23587          * @param {Roo.HtmlEditorCore} this
23588          * @param {String} html
23589          */
23590         beforepush: true,
23591          /**
23592          * @event sync
23593          * Fires when the textarea is updated with content from the editor iframe.
23594          * @param {Roo.HtmlEditorCore} this
23595          * @param {String} html
23596          */
23597         sync: true,
23598          /**
23599          * @event push
23600          * Fires when the iframe editor is updated with content from the textarea.
23601          * @param {Roo.HtmlEditorCore} this
23602          * @param {String} html
23603          */
23604         push: true,
23605         
23606         /**
23607          * @event editorevent
23608          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23609          * @param {Roo.HtmlEditorCore} this
23610          */
23611         editorevent: true
23612         
23613     });
23614     
23615     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23616     
23617     // defaults : white / black...
23618     this.applyBlacklists();
23619     
23620     
23621     
23622 };
23623
23624
23625 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
23626
23627
23628      /**
23629      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
23630      */
23631     
23632     owner : false,
23633     
23634      /**
23635      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23636      *                        Roo.resizable.
23637      */
23638     resizable : false,
23639      /**
23640      * @cfg {Number} height (in pixels)
23641      */   
23642     height: 300,
23643    /**
23644      * @cfg {Number} width (in pixels)
23645      */   
23646     width: 500,
23647     
23648     /**
23649      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23650      * 
23651      */
23652     stylesheets: false,
23653     
23654     // id of frame..
23655     frameId: false,
23656     
23657     // private properties
23658     validationEvent : false,
23659     deferHeight: true,
23660     initialized : false,
23661     activated : false,
23662     sourceEditMode : false,
23663     onFocus : Roo.emptyFn,
23664     iframePad:3,
23665     hideMode:'offsets',
23666     
23667     clearUp: true,
23668     
23669     // blacklist + whitelisted elements..
23670     black: false,
23671     white: false,
23672      
23673     bodyCls : '',
23674
23675     /**
23676      * Protected method that will not generally be called directly. It
23677      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23678      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23679      */
23680     getDocMarkup : function(){
23681         // body styles..
23682         var st = '';
23683         
23684         // inherit styels from page...?? 
23685         if (this.stylesheets === false) {
23686             
23687             Roo.get(document.head).select('style').each(function(node) {
23688                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23689             });
23690             
23691             Roo.get(document.head).select('link').each(function(node) { 
23692                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23693             });
23694             
23695         } else if (!this.stylesheets.length) {
23696                 // simple..
23697                 st = '<style type="text/css">' +
23698                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23699                    '</style>';
23700         } else { 
23701             st = '<style type="text/css">' +
23702                     this.stylesheets +
23703                 '</style>';
23704         }
23705         
23706         st +=  '<style type="text/css">' +
23707             'IMG { cursor: pointer } ' +
23708         '</style>';
23709
23710         var cls = 'roo-htmleditor-body';
23711         
23712         if(this.bodyCls.length){
23713             cls += ' ' + this.bodyCls;
23714         }
23715         
23716         return '<html><head>' + st  +
23717             //<style type="text/css">' +
23718             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23719             //'</style>' +
23720             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
23721     },
23722
23723     // private
23724     onRender : function(ct, position)
23725     {
23726         var _t = this;
23727         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23728         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23729         
23730         
23731         this.el.dom.style.border = '0 none';
23732         this.el.dom.setAttribute('tabIndex', -1);
23733         this.el.addClass('x-hidden hide');
23734         
23735         
23736         
23737         if(Roo.isIE){ // fix IE 1px bogus margin
23738             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23739         }
23740        
23741         
23742         this.frameId = Roo.id();
23743         
23744          
23745         
23746         var iframe = this.owner.wrap.createChild({
23747             tag: 'iframe',
23748             cls: 'form-control', // bootstrap..
23749             id: this.frameId,
23750             name: this.frameId,
23751             frameBorder : 'no',
23752             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
23753         }, this.el
23754         );
23755         
23756         
23757         this.iframe = iframe.dom;
23758
23759          this.assignDocWin();
23760         
23761         this.doc.designMode = 'on';
23762        
23763         this.doc.open();
23764         this.doc.write(this.getDocMarkup());
23765         this.doc.close();
23766
23767         
23768         var task = { // must defer to wait for browser to be ready
23769             run : function(){
23770                 //console.log("run task?" + this.doc.readyState);
23771                 this.assignDocWin();
23772                 if(this.doc.body || this.doc.readyState == 'complete'){
23773                     try {
23774                         this.doc.designMode="on";
23775                     } catch (e) {
23776                         return;
23777                     }
23778                     Roo.TaskMgr.stop(task);
23779                     this.initEditor.defer(10, this);
23780                 }
23781             },
23782             interval : 10,
23783             duration: 10000,
23784             scope: this
23785         };
23786         Roo.TaskMgr.start(task);
23787
23788     },
23789
23790     // private
23791     onResize : function(w, h)
23792     {
23793          Roo.log('resize: ' +w + ',' + h );
23794         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23795         if(!this.iframe){
23796             return;
23797         }
23798         if(typeof w == 'number'){
23799             
23800             this.iframe.style.width = w + 'px';
23801         }
23802         if(typeof h == 'number'){
23803             
23804             this.iframe.style.height = h + 'px';
23805             if(this.doc){
23806                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23807             }
23808         }
23809         
23810     },
23811
23812     /**
23813      * Toggles the editor between standard and source edit mode.
23814      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23815      */
23816     toggleSourceEdit : function(sourceEditMode){
23817         
23818         this.sourceEditMode = sourceEditMode === true;
23819         
23820         if(this.sourceEditMode){
23821  
23822             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
23823             
23824         }else{
23825             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23826             //this.iframe.className = '';
23827             this.deferFocus();
23828         }
23829         //this.setSize(this.owner.wrap.getSize());
23830         //this.fireEvent('editmodechange', this, this.sourceEditMode);
23831     },
23832
23833     
23834   
23835
23836     /**
23837      * Protected method that will not generally be called directly. If you need/want
23838      * custom HTML cleanup, this is the method you should override.
23839      * @param {String} html The HTML to be cleaned
23840      * return {String} The cleaned HTML
23841      */
23842     cleanHtml : function(html){
23843         html = String(html);
23844         if(html.length > 5){
23845             if(Roo.isSafari){ // strip safari nonsense
23846                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23847             }
23848         }
23849         if(html == '&nbsp;'){
23850             html = '';
23851         }
23852         return html;
23853     },
23854
23855     /**
23856      * HTML Editor -> Textarea
23857      * Protected method that will not generally be called directly. Syncs the contents
23858      * of the editor iframe with the textarea.
23859      */
23860     syncValue : function(){
23861         if(this.initialized){
23862             var bd = (this.doc.body || this.doc.documentElement);
23863             //this.cleanUpPaste(); -- this is done else where and causes havoc..
23864             var html = bd.innerHTML;
23865             if(Roo.isSafari){
23866                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23867                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23868                 if(m && m[1]){
23869                     html = '<div style="'+m[0]+'">' + html + '</div>';
23870                 }
23871             }
23872             html = this.cleanHtml(html);
23873             // fix up the special chars.. normaly like back quotes in word...
23874             // however we do not want to do this with chinese..
23875             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23876                 
23877                 var cc = match.charCodeAt();
23878
23879                 // Get the character value, handling surrogate pairs
23880                 if (match.length == 2) {
23881                     // It's a surrogate pair, calculate the Unicode code point
23882                     var high = match.charCodeAt(0) - 0xD800;
23883                     var low  = match.charCodeAt(1) - 0xDC00;
23884                     cc = (high * 0x400) + low + 0x10000;
23885                 }  else if (
23886                     (cc >= 0x4E00 && cc < 0xA000 ) ||
23887                     (cc >= 0x3400 && cc < 0x4E00 ) ||
23888                     (cc >= 0xf900 && cc < 0xfb00 )
23889                 ) {
23890                         return match;
23891                 }  
23892          
23893                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
23894                 return "&#" + cc + ";";
23895                 
23896                 
23897             });
23898             
23899             
23900              
23901             if(this.owner.fireEvent('beforesync', this, html) !== false){
23902                 this.el.dom.value = html;
23903                 this.owner.fireEvent('sync', this, html);
23904             }
23905         }
23906     },
23907
23908     /**
23909      * Protected method that will not generally be called directly. Pushes the value of the textarea
23910      * into the iframe editor.
23911      */
23912     pushValue : function(){
23913         if(this.initialized){
23914             var v = this.el.dom.value.trim();
23915             
23916 //            if(v.length < 1){
23917 //                v = '&#160;';
23918 //            }
23919             
23920             if(this.owner.fireEvent('beforepush', this, v) !== false){
23921                 var d = (this.doc.body || this.doc.documentElement);
23922                 d.innerHTML = v;
23923                 this.cleanUpPaste();
23924                 this.el.dom.value = d.innerHTML;
23925                 this.owner.fireEvent('push', this, v);
23926             }
23927         }
23928     },
23929
23930     // private
23931     deferFocus : function(){
23932         this.focus.defer(10, this);
23933     },
23934
23935     // doc'ed in Field
23936     focus : function(){
23937         if(this.win && !this.sourceEditMode){
23938             this.win.focus();
23939         }else{
23940             this.el.focus();
23941         }
23942     },
23943     
23944     assignDocWin: function()
23945     {
23946         var iframe = this.iframe;
23947         
23948          if(Roo.isIE){
23949             this.doc = iframe.contentWindow.document;
23950             this.win = iframe.contentWindow;
23951         } else {
23952 //            if (!Roo.get(this.frameId)) {
23953 //                return;
23954 //            }
23955 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23956 //            this.win = Roo.get(this.frameId).dom.contentWindow;
23957             
23958             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
23959                 return;
23960             }
23961             
23962             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23963             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
23964         }
23965     },
23966     
23967     // private
23968     initEditor : function(){
23969         //console.log("INIT EDITOR");
23970         this.assignDocWin();
23971         
23972         
23973         
23974         this.doc.designMode="on";
23975         this.doc.open();
23976         this.doc.write(this.getDocMarkup());
23977         this.doc.close();
23978         
23979         var dbody = (this.doc.body || this.doc.documentElement);
23980         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
23981         // this copies styles from the containing element into thsi one..
23982         // not sure why we need all of this..
23983         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
23984         
23985         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
23986         //ss['background-attachment'] = 'fixed'; // w3c
23987         dbody.bgProperties = 'fixed'; // ie
23988         //Roo.DomHelper.applyStyles(dbody, ss);
23989         Roo.EventManager.on(this.doc, {
23990             //'mousedown': this.onEditorEvent,
23991             'mouseup': this.onEditorEvent,
23992             'dblclick': this.onEditorEvent,
23993             'click': this.onEditorEvent,
23994             'keyup': this.onEditorEvent,
23995             buffer:100,
23996             scope: this
23997         });
23998         if(Roo.isGecko){
23999             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24000         }
24001         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24002             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24003         }
24004         this.initialized = true;
24005
24006         this.owner.fireEvent('initialize', this);
24007         this.pushValue();
24008     },
24009
24010     // private
24011     onDestroy : function(){
24012         
24013         
24014         
24015         if(this.rendered){
24016             
24017             //for (var i =0; i < this.toolbars.length;i++) {
24018             //    // fixme - ask toolbars for heights?
24019             //    this.toolbars[i].onDestroy();
24020            // }
24021             
24022             //this.wrap.dom.innerHTML = '';
24023             //this.wrap.remove();
24024         }
24025     },
24026
24027     // private
24028     onFirstFocus : function(){
24029         
24030         this.assignDocWin();
24031         
24032         
24033         this.activated = true;
24034          
24035     
24036         if(Roo.isGecko){ // prevent silly gecko errors
24037             this.win.focus();
24038             var s = this.win.getSelection();
24039             if(!s.focusNode || s.focusNode.nodeType != 3){
24040                 var r = s.getRangeAt(0);
24041                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24042                 r.collapse(true);
24043                 this.deferFocus();
24044             }
24045             try{
24046                 this.execCmd('useCSS', true);
24047                 this.execCmd('styleWithCSS', false);
24048             }catch(e){}
24049         }
24050         this.owner.fireEvent('activate', this);
24051     },
24052
24053     // private
24054     adjustFont: function(btn){
24055         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24056         //if(Roo.isSafari){ // safari
24057         //    adjust *= 2;
24058        // }
24059         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24060         if(Roo.isSafari){ // safari
24061             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24062             v =  (v < 10) ? 10 : v;
24063             v =  (v > 48) ? 48 : v;
24064             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24065             
24066         }
24067         
24068         
24069         v = Math.max(1, v+adjust);
24070         
24071         this.execCmd('FontSize', v  );
24072     },
24073
24074     onEditorEvent : function(e)
24075     {
24076         this.owner.fireEvent('editorevent', this, e);
24077       //  this.updateToolbar();
24078         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24079     },
24080
24081     insertTag : function(tg)
24082     {
24083         // could be a bit smarter... -> wrap the current selected tRoo..
24084         if (tg.toLowerCase() == 'span' ||
24085             tg.toLowerCase() == 'code' ||
24086             tg.toLowerCase() == 'sup' ||
24087             tg.toLowerCase() == 'sub' 
24088             ) {
24089             
24090             range = this.createRange(this.getSelection());
24091             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24092             wrappingNode.appendChild(range.extractContents());
24093             range.insertNode(wrappingNode);
24094
24095             return;
24096             
24097             
24098             
24099         }
24100         this.execCmd("formatblock",   tg);
24101         
24102     },
24103     
24104     insertText : function(txt)
24105     {
24106         
24107         
24108         var range = this.createRange();
24109         range.deleteContents();
24110                //alert(Sender.getAttribute('label'));
24111                
24112         range.insertNode(this.doc.createTextNode(txt));
24113     } ,
24114     
24115      
24116
24117     /**
24118      * Executes a Midas editor command on the editor document and performs necessary focus and
24119      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24120      * @param {String} cmd The Midas command
24121      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24122      */
24123     relayCmd : function(cmd, value){
24124         this.win.focus();
24125         this.execCmd(cmd, value);
24126         this.owner.fireEvent('editorevent', this);
24127         //this.updateToolbar();
24128         this.owner.deferFocus();
24129     },
24130
24131     /**
24132      * Executes a Midas editor command directly on the editor document.
24133      * For visual commands, you should use {@link #relayCmd} instead.
24134      * <b>This should only be called after the editor is initialized.</b>
24135      * @param {String} cmd The Midas command
24136      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24137      */
24138     execCmd : function(cmd, value){
24139         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24140         this.syncValue();
24141     },
24142  
24143  
24144    
24145     /**
24146      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24147      * to insert tRoo.
24148      * @param {String} text | dom node.. 
24149      */
24150     insertAtCursor : function(text)
24151     {
24152         
24153         if(!this.activated){
24154             return;
24155         }
24156         /*
24157         if(Roo.isIE){
24158             this.win.focus();
24159             var r = this.doc.selection.createRange();
24160             if(r){
24161                 r.collapse(true);
24162                 r.pasteHTML(text);
24163                 this.syncValue();
24164                 this.deferFocus();
24165             
24166             }
24167             return;
24168         }
24169         */
24170         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24171             this.win.focus();
24172             
24173             
24174             // from jquery ui (MIT licenced)
24175             var range, node;
24176             var win = this.win;
24177             
24178             if (win.getSelection && win.getSelection().getRangeAt) {
24179                 range = win.getSelection().getRangeAt(0);
24180                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24181                 range.insertNode(node);
24182             } else if (win.document.selection && win.document.selection.createRange) {
24183                 // no firefox support
24184                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24185                 win.document.selection.createRange().pasteHTML(txt);
24186             } else {
24187                 // no firefox support
24188                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24189                 this.execCmd('InsertHTML', txt);
24190             } 
24191             
24192             this.syncValue();
24193             
24194             this.deferFocus();
24195         }
24196     },
24197  // private
24198     mozKeyPress : function(e){
24199         if(e.ctrlKey){
24200             var c = e.getCharCode(), cmd;
24201           
24202             if(c > 0){
24203                 c = String.fromCharCode(c).toLowerCase();
24204                 switch(c){
24205                     case 'b':
24206                         cmd = 'bold';
24207                         break;
24208                     case 'i':
24209                         cmd = 'italic';
24210                         break;
24211                     
24212                     case 'u':
24213                         cmd = 'underline';
24214                         break;
24215                     
24216                     case 'v':
24217                         this.cleanUpPaste.defer(100, this);
24218                         return;
24219                         
24220                 }
24221                 if(cmd){
24222                     this.win.focus();
24223                     this.execCmd(cmd);
24224                     this.deferFocus();
24225                     e.preventDefault();
24226                 }
24227                 
24228             }
24229         }
24230     },
24231
24232     // private
24233     fixKeys : function(){ // load time branching for fastest keydown performance
24234         if(Roo.isIE){
24235             return function(e){
24236                 var k = e.getKey(), r;
24237                 if(k == e.TAB){
24238                     e.stopEvent();
24239                     r = this.doc.selection.createRange();
24240                     if(r){
24241                         r.collapse(true);
24242                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24243                         this.deferFocus();
24244                     }
24245                     return;
24246                 }
24247                 
24248                 if(k == e.ENTER){
24249                     r = this.doc.selection.createRange();
24250                     if(r){
24251                         var target = r.parentElement();
24252                         if(!target || target.tagName.toLowerCase() != 'li'){
24253                             e.stopEvent();
24254                             r.pasteHTML('<br />');
24255                             r.collapse(false);
24256                             r.select();
24257                         }
24258                     }
24259                 }
24260                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24261                     this.cleanUpPaste.defer(100, this);
24262                     return;
24263                 }
24264                 
24265                 
24266             };
24267         }else if(Roo.isOpera){
24268             return function(e){
24269                 var k = e.getKey();
24270                 if(k == e.TAB){
24271                     e.stopEvent();
24272                     this.win.focus();
24273                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24274                     this.deferFocus();
24275                 }
24276                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24277                     this.cleanUpPaste.defer(100, this);
24278                     return;
24279                 }
24280                 
24281             };
24282         }else if(Roo.isSafari){
24283             return function(e){
24284                 var k = e.getKey();
24285                 
24286                 if(k == e.TAB){
24287                     e.stopEvent();
24288                     this.execCmd('InsertText','\t');
24289                     this.deferFocus();
24290                     return;
24291                 }
24292                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24293                     this.cleanUpPaste.defer(100, this);
24294                     return;
24295                 }
24296                 
24297              };
24298         }
24299     }(),
24300     
24301     getAllAncestors: function()
24302     {
24303         var p = this.getSelectedNode();
24304         var a = [];
24305         if (!p) {
24306             a.push(p); // push blank onto stack..
24307             p = this.getParentElement();
24308         }
24309         
24310         
24311         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24312             a.push(p);
24313             p = p.parentNode;
24314         }
24315         a.push(this.doc.body);
24316         return a;
24317     },
24318     lastSel : false,
24319     lastSelNode : false,
24320     
24321     
24322     getSelection : function() 
24323     {
24324         this.assignDocWin();
24325         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24326     },
24327     
24328     getSelectedNode: function() 
24329     {
24330         // this may only work on Gecko!!!
24331         
24332         // should we cache this!!!!
24333         
24334         
24335         
24336          
24337         var range = this.createRange(this.getSelection()).cloneRange();
24338         
24339         if (Roo.isIE) {
24340             var parent = range.parentElement();
24341             while (true) {
24342                 var testRange = range.duplicate();
24343                 testRange.moveToElementText(parent);
24344                 if (testRange.inRange(range)) {
24345                     break;
24346                 }
24347                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24348                     break;
24349                 }
24350                 parent = parent.parentElement;
24351             }
24352             return parent;
24353         }
24354         
24355         // is ancestor a text element.
24356         var ac =  range.commonAncestorContainer;
24357         if (ac.nodeType == 3) {
24358             ac = ac.parentNode;
24359         }
24360         
24361         var ar = ac.childNodes;
24362          
24363         var nodes = [];
24364         var other_nodes = [];
24365         var has_other_nodes = false;
24366         for (var i=0;i<ar.length;i++) {
24367             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24368                 continue;
24369             }
24370             // fullly contained node.
24371             
24372             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24373                 nodes.push(ar[i]);
24374                 continue;
24375             }
24376             
24377             // probably selected..
24378             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24379                 other_nodes.push(ar[i]);
24380                 continue;
24381             }
24382             // outer..
24383             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24384                 continue;
24385             }
24386             
24387             
24388             has_other_nodes = true;
24389         }
24390         if (!nodes.length && other_nodes.length) {
24391             nodes= other_nodes;
24392         }
24393         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24394             return false;
24395         }
24396         
24397         return nodes[0];
24398     },
24399     createRange: function(sel)
24400     {
24401         // this has strange effects when using with 
24402         // top toolbar - not sure if it's a great idea.
24403         //this.editor.contentWindow.focus();
24404         if (typeof sel != "undefined") {
24405             try {
24406                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24407             } catch(e) {
24408                 return this.doc.createRange();
24409             }
24410         } else {
24411             return this.doc.createRange();
24412         }
24413     },
24414     getParentElement: function()
24415     {
24416         
24417         this.assignDocWin();
24418         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24419         
24420         var range = this.createRange(sel);
24421          
24422         try {
24423             var p = range.commonAncestorContainer;
24424             while (p.nodeType == 3) { // text node
24425                 p = p.parentNode;
24426             }
24427             return p;
24428         } catch (e) {
24429             return null;
24430         }
24431     
24432     },
24433     /***
24434      *
24435      * Range intersection.. the hard stuff...
24436      *  '-1' = before
24437      *  '0' = hits..
24438      *  '1' = after.
24439      *         [ -- selected range --- ]
24440      *   [fail]                        [fail]
24441      *
24442      *    basically..
24443      *      if end is before start or  hits it. fail.
24444      *      if start is after end or hits it fail.
24445      *
24446      *   if either hits (but other is outside. - then it's not 
24447      *   
24448      *    
24449      **/
24450     
24451     
24452     // @see http://www.thismuchiknow.co.uk/?p=64.
24453     rangeIntersectsNode : function(range, node)
24454     {
24455         var nodeRange = node.ownerDocument.createRange();
24456         try {
24457             nodeRange.selectNode(node);
24458         } catch (e) {
24459             nodeRange.selectNodeContents(node);
24460         }
24461     
24462         var rangeStartRange = range.cloneRange();
24463         rangeStartRange.collapse(true);
24464     
24465         var rangeEndRange = range.cloneRange();
24466         rangeEndRange.collapse(false);
24467     
24468         var nodeStartRange = nodeRange.cloneRange();
24469         nodeStartRange.collapse(true);
24470     
24471         var nodeEndRange = nodeRange.cloneRange();
24472         nodeEndRange.collapse(false);
24473     
24474         return rangeStartRange.compareBoundaryPoints(
24475                  Range.START_TO_START, nodeEndRange) == -1 &&
24476                rangeEndRange.compareBoundaryPoints(
24477                  Range.START_TO_START, nodeStartRange) == 1;
24478         
24479          
24480     },
24481     rangeCompareNode : function(range, node)
24482     {
24483         var nodeRange = node.ownerDocument.createRange();
24484         try {
24485             nodeRange.selectNode(node);
24486         } catch (e) {
24487             nodeRange.selectNodeContents(node);
24488         }
24489         
24490         
24491         range.collapse(true);
24492     
24493         nodeRange.collapse(true);
24494      
24495         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24496         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24497          
24498         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24499         
24500         var nodeIsBefore   =  ss == 1;
24501         var nodeIsAfter    = ee == -1;
24502         
24503         if (nodeIsBefore && nodeIsAfter) {
24504             return 0; // outer
24505         }
24506         if (!nodeIsBefore && nodeIsAfter) {
24507             return 1; //right trailed.
24508         }
24509         
24510         if (nodeIsBefore && !nodeIsAfter) {
24511             return 2;  // left trailed.
24512         }
24513         // fully contined.
24514         return 3;
24515     },
24516
24517     // private? - in a new class?
24518     cleanUpPaste :  function()
24519     {
24520         // cleans up the whole document..
24521         Roo.log('cleanuppaste');
24522         
24523         this.cleanUpChildren(this.doc.body);
24524         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24525         if (clean != this.doc.body.innerHTML) {
24526             this.doc.body.innerHTML = clean;
24527         }
24528         
24529     },
24530     
24531     cleanWordChars : function(input) {// change the chars to hex code
24532         var he = Roo.HtmlEditorCore;
24533         
24534         var output = input;
24535         Roo.each(he.swapCodes, function(sw) { 
24536             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24537             
24538             output = output.replace(swapper, sw[1]);
24539         });
24540         
24541         return output;
24542     },
24543     
24544     
24545     cleanUpChildren : function (n)
24546     {
24547         if (!n.childNodes.length) {
24548             return;
24549         }
24550         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24551            this.cleanUpChild(n.childNodes[i]);
24552         }
24553     },
24554     
24555     
24556         
24557     
24558     cleanUpChild : function (node)
24559     {
24560         var ed = this;
24561         //console.log(node);
24562         if (node.nodeName == "#text") {
24563             // clean up silly Windows -- stuff?
24564             return; 
24565         }
24566         if (node.nodeName == "#comment") {
24567             node.parentNode.removeChild(node);
24568             // clean up silly Windows -- stuff?
24569             return; 
24570         }
24571         var lcname = node.tagName.toLowerCase();
24572         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24573         // whitelist of tags..
24574         
24575         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24576             // remove node.
24577             node.parentNode.removeChild(node);
24578             return;
24579             
24580         }
24581         
24582         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24583         
24584         // spans with no attributes - just remove them..
24585         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24586             remove_keep_children = true;
24587         }
24588         
24589         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24590         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24591         
24592         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24593         //    remove_keep_children = true;
24594         //}
24595         
24596         if (remove_keep_children) {
24597             this.cleanUpChildren(node);
24598             // inserts everything just before this node...
24599             while (node.childNodes.length) {
24600                 var cn = node.childNodes[0];
24601                 node.removeChild(cn);
24602                 node.parentNode.insertBefore(cn, node);
24603             }
24604             node.parentNode.removeChild(node);
24605             return;
24606         }
24607         
24608         if (!node.attributes || !node.attributes.length) {
24609             
24610           
24611             
24612             
24613             this.cleanUpChildren(node);
24614             return;
24615         }
24616         
24617         function cleanAttr(n,v)
24618         {
24619             
24620             if (v.match(/^\./) || v.match(/^\//)) {
24621                 return;
24622             }
24623             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24624                 return;
24625             }
24626             if (v.match(/^#/)) {
24627                 return;
24628             }
24629 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24630             node.removeAttribute(n);
24631             
24632         }
24633         
24634         var cwhite = this.cwhite;
24635         var cblack = this.cblack;
24636             
24637         function cleanStyle(n,v)
24638         {
24639             if (v.match(/expression/)) { //XSS?? should we even bother..
24640                 node.removeAttribute(n);
24641                 return;
24642             }
24643             
24644             var parts = v.split(/;/);
24645             var clean = [];
24646             
24647             Roo.each(parts, function(p) {
24648                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24649                 if (!p.length) {
24650                     return true;
24651                 }
24652                 var l = p.split(':').shift().replace(/\s+/g,'');
24653                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24654                 
24655                 if ( cwhite.length && cblack.indexOf(l) > -1) {
24656 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24657                     //node.removeAttribute(n);
24658                     return true;
24659                 }
24660                 //Roo.log()
24661                 // only allow 'c whitelisted system attributes'
24662                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
24663 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24664                     //node.removeAttribute(n);
24665                     return true;
24666                 }
24667                 
24668                 
24669                  
24670                 
24671                 clean.push(p);
24672                 return true;
24673             });
24674             if (clean.length) { 
24675                 node.setAttribute(n, clean.join(';'));
24676             } else {
24677                 node.removeAttribute(n);
24678             }
24679             
24680         }
24681         
24682         
24683         for (var i = node.attributes.length-1; i > -1 ; i--) {
24684             var a = node.attributes[i];
24685             //console.log(a);
24686             
24687             if (a.name.toLowerCase().substr(0,2)=='on')  {
24688                 node.removeAttribute(a.name);
24689                 continue;
24690             }
24691             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24692                 node.removeAttribute(a.name);
24693                 continue;
24694             }
24695             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24696                 cleanAttr(a.name,a.value); // fixme..
24697                 continue;
24698             }
24699             if (a.name == 'style') {
24700                 cleanStyle(a.name,a.value);
24701                 continue;
24702             }
24703             /// clean up MS crap..
24704             // tecnically this should be a list of valid class'es..
24705             
24706             
24707             if (a.name == 'class') {
24708                 if (a.value.match(/^Mso/)) {
24709                     node.removeAttribute('class');
24710                 }
24711                 
24712                 if (a.value.match(/^body$/)) {
24713                     node.removeAttribute('class');
24714                 }
24715                 continue;
24716             }
24717             
24718             // style cleanup!?
24719             // class cleanup?
24720             
24721         }
24722         
24723         
24724         this.cleanUpChildren(node);
24725         
24726         
24727     },
24728     
24729     /**
24730      * Clean up MS wordisms...
24731      */
24732     cleanWord : function(node)
24733     {
24734         if (!node) {
24735             this.cleanWord(this.doc.body);
24736             return;
24737         }
24738         
24739         if(
24740                 node.nodeName == 'SPAN' &&
24741                 !node.hasAttributes() &&
24742                 node.childNodes.length == 1 &&
24743                 node.firstChild.nodeName == "#text"  
24744         ) {
24745             var textNode = node.firstChild;
24746             node.removeChild(textNode);
24747             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24748                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
24749             }
24750             node.parentNode.insertBefore(textNode, node);
24751             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24752                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24753             }
24754             node.parentNode.removeChild(node);
24755         }
24756         
24757         if (node.nodeName == "#text") {
24758             // clean up silly Windows -- stuff?
24759             return; 
24760         }
24761         if (node.nodeName == "#comment") {
24762             node.parentNode.removeChild(node);
24763             // clean up silly Windows -- stuff?
24764             return; 
24765         }
24766         
24767         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24768             node.parentNode.removeChild(node);
24769             return;
24770         }
24771         //Roo.log(node.tagName);
24772         // remove - but keep children..
24773         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24774             //Roo.log('-- removed');
24775             while (node.childNodes.length) {
24776                 var cn = node.childNodes[0];
24777                 node.removeChild(cn);
24778                 node.parentNode.insertBefore(cn, node);
24779                 // move node to parent - and clean it..
24780                 this.cleanWord(cn);
24781             }
24782             node.parentNode.removeChild(node);
24783             /// no need to iterate chidlren = it's got none..
24784             //this.iterateChildren(node, this.cleanWord);
24785             return;
24786         }
24787         // clean styles
24788         if (node.className.length) {
24789             
24790             var cn = node.className.split(/\W+/);
24791             var cna = [];
24792             Roo.each(cn, function(cls) {
24793                 if (cls.match(/Mso[a-zA-Z]+/)) {
24794                     return;
24795                 }
24796                 cna.push(cls);
24797             });
24798             node.className = cna.length ? cna.join(' ') : '';
24799             if (!cna.length) {
24800                 node.removeAttribute("class");
24801             }
24802         }
24803         
24804         if (node.hasAttribute("lang")) {
24805             node.removeAttribute("lang");
24806         }
24807         
24808         if (node.hasAttribute("style")) {
24809             
24810             var styles = node.getAttribute("style").split(";");
24811             var nstyle = [];
24812             Roo.each(styles, function(s) {
24813                 if (!s.match(/:/)) {
24814                     return;
24815                 }
24816                 var kv = s.split(":");
24817                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24818                     return;
24819                 }
24820                 // what ever is left... we allow.
24821                 nstyle.push(s);
24822             });
24823             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24824             if (!nstyle.length) {
24825                 node.removeAttribute('style');
24826             }
24827         }
24828         this.iterateChildren(node, this.cleanWord);
24829         
24830         
24831         
24832     },
24833     /**
24834      * iterateChildren of a Node, calling fn each time, using this as the scole..
24835      * @param {DomNode} node node to iterate children of.
24836      * @param {Function} fn method of this class to call on each item.
24837      */
24838     iterateChildren : function(node, fn)
24839     {
24840         if (!node.childNodes.length) {
24841                 return;
24842         }
24843         for (var i = node.childNodes.length-1; i > -1 ; i--) {
24844            fn.call(this, node.childNodes[i])
24845         }
24846     },
24847     
24848     
24849     /**
24850      * cleanTableWidths.
24851      *
24852      * Quite often pasting from word etc.. results in tables with column and widths.
24853      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24854      *
24855      */
24856     cleanTableWidths : function(node)
24857     {
24858          
24859          
24860         if (!node) {
24861             this.cleanTableWidths(this.doc.body);
24862             return;
24863         }
24864         
24865         // ignore list...
24866         if (node.nodeName == "#text" || node.nodeName == "#comment") {
24867             return; 
24868         }
24869         Roo.log(node.tagName);
24870         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24871             this.iterateChildren(node, this.cleanTableWidths);
24872             return;
24873         }
24874         if (node.hasAttribute('width')) {
24875             node.removeAttribute('width');
24876         }
24877         
24878          
24879         if (node.hasAttribute("style")) {
24880             // pretty basic...
24881             
24882             var styles = node.getAttribute("style").split(";");
24883             var nstyle = [];
24884             Roo.each(styles, function(s) {
24885                 if (!s.match(/:/)) {
24886                     return;
24887                 }
24888                 var kv = s.split(":");
24889                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
24890                     return;
24891                 }
24892                 // what ever is left... we allow.
24893                 nstyle.push(s);
24894             });
24895             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24896             if (!nstyle.length) {
24897                 node.removeAttribute('style');
24898             }
24899         }
24900         
24901         this.iterateChildren(node, this.cleanTableWidths);
24902         
24903         
24904     },
24905     
24906     
24907     
24908     
24909     domToHTML : function(currentElement, depth, nopadtext) {
24910         
24911         depth = depth || 0;
24912         nopadtext = nopadtext || false;
24913     
24914         if (!currentElement) {
24915             return this.domToHTML(this.doc.body);
24916         }
24917         
24918         //Roo.log(currentElement);
24919         var j;
24920         var allText = false;
24921         var nodeName = currentElement.nodeName;
24922         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
24923         
24924         if  (nodeName == '#text') {
24925             
24926             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
24927         }
24928         
24929         
24930         var ret = '';
24931         if (nodeName != 'BODY') {
24932              
24933             var i = 0;
24934             // Prints the node tagName, such as <A>, <IMG>, etc
24935             if (tagName) {
24936                 var attr = [];
24937                 for(i = 0; i < currentElement.attributes.length;i++) {
24938                     // quoting?
24939                     var aname = currentElement.attributes.item(i).name;
24940                     if (!currentElement.attributes.item(i).value.length) {
24941                         continue;
24942                     }
24943                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
24944                 }
24945                 
24946                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
24947             } 
24948             else {
24949                 
24950                 // eack
24951             }
24952         } else {
24953             tagName = false;
24954         }
24955         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
24956             return ret;
24957         }
24958         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
24959             nopadtext = true;
24960         }
24961         
24962         
24963         // Traverse the tree
24964         i = 0;
24965         var currentElementChild = currentElement.childNodes.item(i);
24966         var allText = true;
24967         var innerHTML  = '';
24968         lastnode = '';
24969         while (currentElementChild) {
24970             // Formatting code (indent the tree so it looks nice on the screen)
24971             var nopad = nopadtext;
24972             if (lastnode == 'SPAN') {
24973                 nopad  = true;
24974             }
24975             // text
24976             if  (currentElementChild.nodeName == '#text') {
24977                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
24978                 toadd = nopadtext ? toadd : toadd.trim();
24979                 if (!nopad && toadd.length > 80) {
24980                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
24981                 }
24982                 innerHTML  += toadd;
24983                 
24984                 i++;
24985                 currentElementChild = currentElement.childNodes.item(i);
24986                 lastNode = '';
24987                 continue;
24988             }
24989             allText = false;
24990             
24991             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
24992                 
24993             // Recursively traverse the tree structure of the child node
24994             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
24995             lastnode = currentElementChild.nodeName;
24996             i++;
24997             currentElementChild=currentElement.childNodes.item(i);
24998         }
24999         
25000         ret += innerHTML;
25001         
25002         if (!allText) {
25003                 // The remaining code is mostly for formatting the tree
25004             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25005         }
25006         
25007         
25008         if (tagName) {
25009             ret+= "</"+tagName+">";
25010         }
25011         return ret;
25012         
25013     },
25014         
25015     applyBlacklists : function()
25016     {
25017         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25018         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25019         
25020         this.white = [];
25021         this.black = [];
25022         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25023             if (b.indexOf(tag) > -1) {
25024                 return;
25025             }
25026             this.white.push(tag);
25027             
25028         }, this);
25029         
25030         Roo.each(w, function(tag) {
25031             if (b.indexOf(tag) > -1) {
25032                 return;
25033             }
25034             if (this.white.indexOf(tag) > -1) {
25035                 return;
25036             }
25037             this.white.push(tag);
25038             
25039         }, this);
25040         
25041         
25042         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25043             if (w.indexOf(tag) > -1) {
25044                 return;
25045             }
25046             this.black.push(tag);
25047             
25048         }, this);
25049         
25050         Roo.each(b, function(tag) {
25051             if (w.indexOf(tag) > -1) {
25052                 return;
25053             }
25054             if (this.black.indexOf(tag) > -1) {
25055                 return;
25056             }
25057             this.black.push(tag);
25058             
25059         }, this);
25060         
25061         
25062         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25063         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25064         
25065         this.cwhite = [];
25066         this.cblack = [];
25067         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25068             if (b.indexOf(tag) > -1) {
25069                 return;
25070             }
25071             this.cwhite.push(tag);
25072             
25073         }, this);
25074         
25075         Roo.each(w, function(tag) {
25076             if (b.indexOf(tag) > -1) {
25077                 return;
25078             }
25079             if (this.cwhite.indexOf(tag) > -1) {
25080                 return;
25081             }
25082             this.cwhite.push(tag);
25083             
25084         }, this);
25085         
25086         
25087         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25088             if (w.indexOf(tag) > -1) {
25089                 return;
25090             }
25091             this.cblack.push(tag);
25092             
25093         }, this);
25094         
25095         Roo.each(b, function(tag) {
25096             if (w.indexOf(tag) > -1) {
25097                 return;
25098             }
25099             if (this.cblack.indexOf(tag) > -1) {
25100                 return;
25101             }
25102             this.cblack.push(tag);
25103             
25104         }, this);
25105     },
25106     
25107     setStylesheets : function(stylesheets)
25108     {
25109         if(typeof(stylesheets) == 'string'){
25110             Roo.get(this.iframe.contentDocument.head).createChild({
25111                 tag : 'link',
25112                 rel : 'stylesheet',
25113                 type : 'text/css',
25114                 href : stylesheets
25115             });
25116             
25117             return;
25118         }
25119         var _this = this;
25120      
25121         Roo.each(stylesheets, function(s) {
25122             if(!s.length){
25123                 return;
25124             }
25125             
25126             Roo.get(_this.iframe.contentDocument.head).createChild({
25127                 tag : 'link',
25128                 rel : 'stylesheet',
25129                 type : 'text/css',
25130                 href : s
25131             });
25132         });
25133
25134         
25135     },
25136     
25137     removeStylesheets : function()
25138     {
25139         var _this = this;
25140         
25141         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25142             s.remove();
25143         });
25144     },
25145     
25146     setStyle : function(style)
25147     {
25148         Roo.get(this.iframe.contentDocument.head).createChild({
25149             tag : 'style',
25150             type : 'text/css',
25151             html : style
25152         });
25153
25154         return;
25155     }
25156     
25157     // hide stuff that is not compatible
25158     /**
25159      * @event blur
25160      * @hide
25161      */
25162     /**
25163      * @event change
25164      * @hide
25165      */
25166     /**
25167      * @event focus
25168      * @hide
25169      */
25170     /**
25171      * @event specialkey
25172      * @hide
25173      */
25174     /**
25175      * @cfg {String} fieldClass @hide
25176      */
25177     /**
25178      * @cfg {String} focusClass @hide
25179      */
25180     /**
25181      * @cfg {String} autoCreate @hide
25182      */
25183     /**
25184      * @cfg {String} inputType @hide
25185      */
25186     /**
25187      * @cfg {String} invalidClass @hide
25188      */
25189     /**
25190      * @cfg {String} invalidText @hide
25191      */
25192     /**
25193      * @cfg {String} msgFx @hide
25194      */
25195     /**
25196      * @cfg {String} validateOnBlur @hide
25197      */
25198 });
25199
25200 Roo.HtmlEditorCore.white = [
25201         'area', 'br', 'img', 'input', 'hr', 'wbr',
25202         
25203        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25204        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25205        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25206        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25207        'table',   'ul',         'xmp', 
25208        
25209        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25210       'thead',   'tr', 
25211      
25212       'dir', 'menu', 'ol', 'ul', 'dl',
25213        
25214       'embed',  'object'
25215 ];
25216
25217
25218 Roo.HtmlEditorCore.black = [
25219     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25220         'applet', // 
25221         'base',   'basefont', 'bgsound', 'blink',  'body', 
25222         'frame',  'frameset', 'head',    'html',   'ilayer', 
25223         'iframe', 'layer',  'link',     'meta',    'object',   
25224         'script', 'style' ,'title',  'xml' // clean later..
25225 ];
25226 Roo.HtmlEditorCore.clean = [
25227     'script', 'style', 'title', 'xml'
25228 ];
25229 Roo.HtmlEditorCore.remove = [
25230     'font'
25231 ];
25232 // attributes..
25233
25234 Roo.HtmlEditorCore.ablack = [
25235     'on'
25236 ];
25237     
25238 Roo.HtmlEditorCore.aclean = [ 
25239     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25240 ];
25241
25242 // protocols..
25243 Roo.HtmlEditorCore.pwhite= [
25244         'http',  'https',  'mailto'
25245 ];
25246
25247 // white listed style attributes.
25248 Roo.HtmlEditorCore.cwhite= [
25249       //  'text-align', /// default is to allow most things..
25250       
25251          
25252 //        'font-size'//??
25253 ];
25254
25255 // black listed style attributes.
25256 Roo.HtmlEditorCore.cblack= [
25257       //  'font-size' -- this can be set by the project 
25258 ];
25259
25260
25261 Roo.HtmlEditorCore.swapCodes   =[ 
25262     [    8211, "--" ], 
25263     [    8212, "--" ], 
25264     [    8216,  "'" ],  
25265     [    8217, "'" ],  
25266     [    8220, '"' ],  
25267     [    8221, '"' ],  
25268     [    8226, "*" ],  
25269     [    8230, "..." ]
25270 ]; 
25271
25272     /*
25273  * - LGPL
25274  *
25275  * HtmlEditor
25276  * 
25277  */
25278
25279 /**
25280  * @class Roo.bootstrap.HtmlEditor
25281  * @extends Roo.bootstrap.TextArea
25282  * Bootstrap HtmlEditor class
25283
25284  * @constructor
25285  * Create a new HtmlEditor
25286  * @param {Object} config The config object
25287  */
25288
25289 Roo.bootstrap.HtmlEditor = function(config){
25290     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25291     if (!this.toolbars) {
25292         this.toolbars = [];
25293     }
25294     
25295     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25296     this.addEvents({
25297             /**
25298              * @event initialize
25299              * Fires when the editor is fully initialized (including the iframe)
25300              * @param {HtmlEditor} this
25301              */
25302             initialize: true,
25303             /**
25304              * @event activate
25305              * Fires when the editor is first receives the focus. Any insertion must wait
25306              * until after this event.
25307              * @param {HtmlEditor} this
25308              */
25309             activate: true,
25310              /**
25311              * @event beforesync
25312              * Fires before the textarea is updated with content from the editor iframe. Return false
25313              * to cancel the sync.
25314              * @param {HtmlEditor} this
25315              * @param {String} html
25316              */
25317             beforesync: true,
25318              /**
25319              * @event beforepush
25320              * Fires before the iframe editor is updated with content from the textarea. Return false
25321              * to cancel the push.
25322              * @param {HtmlEditor} this
25323              * @param {String} html
25324              */
25325             beforepush: true,
25326              /**
25327              * @event sync
25328              * Fires when the textarea is updated with content from the editor iframe.
25329              * @param {HtmlEditor} this
25330              * @param {String} html
25331              */
25332             sync: true,
25333              /**
25334              * @event push
25335              * Fires when the iframe editor is updated with content from the textarea.
25336              * @param {HtmlEditor} this
25337              * @param {String} html
25338              */
25339             push: true,
25340              /**
25341              * @event editmodechange
25342              * Fires when the editor switches edit modes
25343              * @param {HtmlEditor} this
25344              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25345              */
25346             editmodechange: true,
25347             /**
25348              * @event editorevent
25349              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25350              * @param {HtmlEditor} this
25351              */
25352             editorevent: true,
25353             /**
25354              * @event firstfocus
25355              * Fires when on first focus - needed by toolbars..
25356              * @param {HtmlEditor} this
25357              */
25358             firstfocus: true,
25359             /**
25360              * @event autosave
25361              * Auto save the htmlEditor value as a file into Events
25362              * @param {HtmlEditor} this
25363              */
25364             autosave: true,
25365             /**
25366              * @event savedpreview
25367              * preview the saved version of htmlEditor
25368              * @param {HtmlEditor} this
25369              */
25370             savedpreview: true
25371         });
25372 };
25373
25374
25375 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25376     
25377     
25378       /**
25379      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25380      */
25381     toolbars : false,
25382     
25383      /**
25384     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25385     */
25386     btns : [],
25387    
25388      /**
25389      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25390      *                        Roo.resizable.
25391      */
25392     resizable : false,
25393      /**
25394      * @cfg {Number} height (in pixels)
25395      */   
25396     height: 300,
25397    /**
25398      * @cfg {Number} width (in pixels)
25399      */   
25400     width: false,
25401     
25402     /**
25403      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25404      * 
25405      */
25406     stylesheets: false,
25407     
25408     // id of frame..
25409     frameId: false,
25410     
25411     // private properties
25412     validationEvent : false,
25413     deferHeight: true,
25414     initialized : false,
25415     activated : false,
25416     
25417     onFocus : Roo.emptyFn,
25418     iframePad:3,
25419     hideMode:'offsets',
25420     
25421     tbContainer : false,
25422     
25423     bodyCls : '',
25424     
25425     toolbarContainer :function() {
25426         return this.wrap.select('.x-html-editor-tb',true).first();
25427     },
25428
25429     /**
25430      * Protected method that will not generally be called directly. It
25431      * is called when the editor creates its toolbar. Override this method if you need to
25432      * add custom toolbar buttons.
25433      * @param {HtmlEditor} editor
25434      */
25435     createToolbar : function(){
25436         Roo.log('renewing');
25437         Roo.log("create toolbars");
25438         
25439         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25440         this.toolbars[0].render(this.toolbarContainer());
25441         
25442         return;
25443         
25444 //        if (!editor.toolbars || !editor.toolbars.length) {
25445 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25446 //        }
25447 //        
25448 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25449 //            editor.toolbars[i] = Roo.factory(
25450 //                    typeof(editor.toolbars[i]) == 'string' ?
25451 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25452 //                Roo.bootstrap.HtmlEditor);
25453 //            editor.toolbars[i].init(editor);
25454 //        }
25455     },
25456
25457      
25458     // private
25459     onRender : function(ct, position)
25460     {
25461        // Roo.log("Call onRender: " + this.xtype);
25462         var _t = this;
25463         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25464       
25465         this.wrap = this.inputEl().wrap({
25466             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25467         });
25468         
25469         this.editorcore.onRender(ct, position);
25470          
25471         if (this.resizable) {
25472             this.resizeEl = new Roo.Resizable(this.wrap, {
25473                 pinned : true,
25474                 wrap: true,
25475                 dynamic : true,
25476                 minHeight : this.height,
25477                 height: this.height,
25478                 handles : this.resizable,
25479                 width: this.width,
25480                 listeners : {
25481                     resize : function(r, w, h) {
25482                         _t.onResize(w,h); // -something
25483                     }
25484                 }
25485             });
25486             
25487         }
25488         this.createToolbar(this);
25489        
25490         
25491         if(!this.width && this.resizable){
25492             this.setSize(this.wrap.getSize());
25493         }
25494         if (this.resizeEl) {
25495             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25496             // should trigger onReize..
25497         }
25498         
25499     },
25500
25501     // private
25502     onResize : function(w, h)
25503     {
25504         Roo.log('resize: ' +w + ',' + h );
25505         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25506         var ew = false;
25507         var eh = false;
25508         
25509         if(this.inputEl() ){
25510             if(typeof w == 'number'){
25511                 var aw = w - this.wrap.getFrameWidth('lr');
25512                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25513                 ew = aw;
25514             }
25515             if(typeof h == 'number'){
25516                  var tbh = -11;  // fixme it needs to tool bar size!
25517                 for (var i =0; i < this.toolbars.length;i++) {
25518                     // fixme - ask toolbars for heights?
25519                     tbh += this.toolbars[i].el.getHeight();
25520                     //if (this.toolbars[i].footer) {
25521                     //    tbh += this.toolbars[i].footer.el.getHeight();
25522                     //}
25523                 }
25524               
25525                 
25526                 
25527                 
25528                 
25529                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25530                 ah -= 5; // knock a few pixes off for look..
25531                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25532                 var eh = ah;
25533             }
25534         }
25535         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25536         this.editorcore.onResize(ew,eh);
25537         
25538     },
25539
25540     /**
25541      * Toggles the editor between standard and source edit mode.
25542      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25543      */
25544     toggleSourceEdit : function(sourceEditMode)
25545     {
25546         this.editorcore.toggleSourceEdit(sourceEditMode);
25547         
25548         if(this.editorcore.sourceEditMode){
25549             Roo.log('editor - showing textarea');
25550             
25551 //            Roo.log('in');
25552 //            Roo.log(this.syncValue());
25553             this.syncValue();
25554             this.inputEl().removeClass(['hide', 'x-hidden']);
25555             this.inputEl().dom.removeAttribute('tabIndex');
25556             this.inputEl().focus();
25557         }else{
25558             Roo.log('editor - hiding textarea');
25559 //            Roo.log('out')
25560 //            Roo.log(this.pushValue()); 
25561             this.pushValue();
25562             
25563             this.inputEl().addClass(['hide', 'x-hidden']);
25564             this.inputEl().dom.setAttribute('tabIndex', -1);
25565             //this.deferFocus();
25566         }
25567          
25568         if(this.resizable){
25569             this.setSize(this.wrap.getSize());
25570         }
25571         
25572         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25573     },
25574  
25575     // private (for BoxComponent)
25576     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25577
25578     // private (for BoxComponent)
25579     getResizeEl : function(){
25580         return this.wrap;
25581     },
25582
25583     // private (for BoxComponent)
25584     getPositionEl : function(){
25585         return this.wrap;
25586     },
25587
25588     // private
25589     initEvents : function(){
25590         this.originalValue = this.getValue();
25591     },
25592
25593 //    /**
25594 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25595 //     * @method
25596 //     */
25597 //    markInvalid : Roo.emptyFn,
25598 //    /**
25599 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25600 //     * @method
25601 //     */
25602 //    clearInvalid : Roo.emptyFn,
25603
25604     setValue : function(v){
25605         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25606         this.editorcore.pushValue();
25607     },
25608
25609      
25610     // private
25611     deferFocus : function(){
25612         this.focus.defer(10, this);
25613     },
25614
25615     // doc'ed in Field
25616     focus : function(){
25617         this.editorcore.focus();
25618         
25619     },
25620       
25621
25622     // private
25623     onDestroy : function(){
25624         
25625         
25626         
25627         if(this.rendered){
25628             
25629             for (var i =0; i < this.toolbars.length;i++) {
25630                 // fixme - ask toolbars for heights?
25631                 this.toolbars[i].onDestroy();
25632             }
25633             
25634             this.wrap.dom.innerHTML = '';
25635             this.wrap.remove();
25636         }
25637     },
25638
25639     // private
25640     onFirstFocus : function(){
25641         //Roo.log("onFirstFocus");
25642         this.editorcore.onFirstFocus();
25643          for (var i =0; i < this.toolbars.length;i++) {
25644             this.toolbars[i].onFirstFocus();
25645         }
25646         
25647     },
25648     
25649     // private
25650     syncValue : function()
25651     {   
25652         this.editorcore.syncValue();
25653     },
25654     
25655     pushValue : function()
25656     {   
25657         this.editorcore.pushValue();
25658     }
25659      
25660     
25661     // hide stuff that is not compatible
25662     /**
25663      * @event blur
25664      * @hide
25665      */
25666     /**
25667      * @event change
25668      * @hide
25669      */
25670     /**
25671      * @event focus
25672      * @hide
25673      */
25674     /**
25675      * @event specialkey
25676      * @hide
25677      */
25678     /**
25679      * @cfg {String} fieldClass @hide
25680      */
25681     /**
25682      * @cfg {String} focusClass @hide
25683      */
25684     /**
25685      * @cfg {String} autoCreate @hide
25686      */
25687     /**
25688      * @cfg {String} inputType @hide
25689      */
25690      
25691     /**
25692      * @cfg {String} invalidText @hide
25693      */
25694     /**
25695      * @cfg {String} msgFx @hide
25696      */
25697     /**
25698      * @cfg {String} validateOnBlur @hide
25699      */
25700 });
25701  
25702     
25703    
25704    
25705    
25706       
25707 Roo.namespace('Roo.bootstrap.htmleditor');
25708 /**
25709  * @class Roo.bootstrap.HtmlEditorToolbar1
25710  * Basic Toolbar
25711  * 
25712  * @example
25713  * Usage:
25714  *
25715  new Roo.bootstrap.HtmlEditor({
25716     ....
25717     toolbars : [
25718         new Roo.bootstrap.HtmlEditorToolbar1({
25719             disable : { fonts: 1 , format: 1, ..., ... , ...],
25720             btns : [ .... ]
25721         })
25722     }
25723      
25724  * 
25725  * @cfg {Object} disable List of elements to disable..
25726  * @cfg {Array} btns List of additional buttons.
25727  * 
25728  * 
25729  * NEEDS Extra CSS? 
25730  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25731  */
25732  
25733 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25734 {
25735     
25736     Roo.apply(this, config);
25737     
25738     // default disabled, based on 'good practice'..
25739     this.disable = this.disable || {};
25740     Roo.applyIf(this.disable, {
25741         fontSize : true,
25742         colors : true,
25743         specialElements : true
25744     });
25745     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25746     
25747     this.editor = config.editor;
25748     this.editorcore = config.editor.editorcore;
25749     
25750     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25751     
25752     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25753     // dont call parent... till later.
25754 }
25755 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
25756      
25757     bar : true,
25758     
25759     editor : false,
25760     editorcore : false,
25761     
25762     
25763     formats : [
25764         "p" ,  
25765         "h1","h2","h3","h4","h5","h6", 
25766         "pre", "code", 
25767         "abbr", "acronym", "address", "cite", "samp", "var",
25768         'div','span'
25769     ],
25770     
25771     onRender : function(ct, position)
25772     {
25773        // Roo.log("Call onRender: " + this.xtype);
25774         
25775        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25776        Roo.log(this.el);
25777        this.el.dom.style.marginBottom = '0';
25778        var _this = this;
25779        var editorcore = this.editorcore;
25780        var editor= this.editor;
25781        
25782        var children = [];
25783        var btn = function(id,cmd , toggle, handler, html){
25784        
25785             var  event = toggle ? 'toggle' : 'click';
25786        
25787             var a = {
25788                 size : 'sm',
25789                 xtype: 'Button',
25790                 xns: Roo.bootstrap,
25791                 //glyphicon : id,
25792                 fa: id,
25793                 cmd : id || cmd,
25794                 enableToggle:toggle !== false,
25795                 html : html || '',
25796                 pressed : toggle ? false : null,
25797                 listeners : {}
25798             };
25799             a.listeners[toggle ? 'toggle' : 'click'] = function() {
25800                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
25801             };
25802             children.push(a);
25803             return a;
25804        }
25805        
25806     //    var cb_box = function...
25807         
25808         var style = {
25809                 xtype: 'Button',
25810                 size : 'sm',
25811                 xns: Roo.bootstrap,
25812                 fa : 'font',
25813                 //html : 'submit'
25814                 menu : {
25815                     xtype: 'Menu',
25816                     xns: Roo.bootstrap,
25817                     items:  []
25818                 }
25819         };
25820         Roo.each(this.formats, function(f) {
25821             style.menu.items.push({
25822                 xtype :'MenuItem',
25823                 xns: Roo.bootstrap,
25824                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25825                 tagname : f,
25826                 listeners : {
25827                     click : function()
25828                     {
25829                         editorcore.insertTag(this.tagname);
25830                         editor.focus();
25831                     }
25832                 }
25833                 
25834             });
25835         });
25836         children.push(style);   
25837         
25838         btn('bold',false,true);
25839         btn('italic',false,true);
25840         btn('align-left', 'justifyleft',true);
25841         btn('align-center', 'justifycenter',true);
25842         btn('align-right' , 'justifyright',true);
25843         btn('link', false, false, function(btn) {
25844             //Roo.log("create link?");
25845             var url = prompt(this.createLinkText, this.defaultLinkValue);
25846             if(url && url != 'http:/'+'/'){
25847                 this.editorcore.relayCmd('createlink', url);
25848             }
25849         }),
25850         btn('list','insertunorderedlist',true);
25851         btn('pencil', false,true, function(btn){
25852                 Roo.log(this);
25853                 this.toggleSourceEdit(btn.pressed);
25854         });
25855         
25856         if (this.editor.btns.length > 0) {
25857             for (var i = 0; i<this.editor.btns.length; i++) {
25858                 children.push(this.editor.btns[i]);
25859             }
25860         }
25861         
25862         /*
25863         var cog = {
25864                 xtype: 'Button',
25865                 size : 'sm',
25866                 xns: Roo.bootstrap,
25867                 glyphicon : 'cog',
25868                 //html : 'submit'
25869                 menu : {
25870                     xtype: 'Menu',
25871                     xns: Roo.bootstrap,
25872                     items:  []
25873                 }
25874         };
25875         
25876         cog.menu.items.push({
25877             xtype :'MenuItem',
25878             xns: Roo.bootstrap,
25879             html : Clean styles,
25880             tagname : f,
25881             listeners : {
25882                 click : function()
25883                 {
25884                     editorcore.insertTag(this.tagname);
25885                     editor.focus();
25886                 }
25887             }
25888             
25889         });
25890        */
25891         
25892          
25893        this.xtype = 'NavSimplebar';
25894         
25895         for(var i=0;i< children.length;i++) {
25896             
25897             this.buttons.add(this.addxtypeChild(children[i]));
25898             
25899         }
25900         
25901         editor.on('editorevent', this.updateToolbar, this);
25902     },
25903     onBtnClick : function(id)
25904     {
25905        this.editorcore.relayCmd(id);
25906        this.editorcore.focus();
25907     },
25908     
25909     /**
25910      * Protected method that will not generally be called directly. It triggers
25911      * a toolbar update by reading the markup state of the current selection in the editor.
25912      */
25913     updateToolbar: function(){
25914
25915         if(!this.editorcore.activated){
25916             this.editor.onFirstFocus(); // is this neeed?
25917             return;
25918         }
25919
25920         var btns = this.buttons; 
25921         var doc = this.editorcore.doc;
25922         btns.get('bold').setActive(doc.queryCommandState('bold'));
25923         btns.get('italic').setActive(doc.queryCommandState('italic'));
25924         //btns.get('underline').setActive(doc.queryCommandState('underline'));
25925         
25926         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
25927         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
25928         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
25929         
25930         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
25931         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
25932          /*
25933         
25934         var ans = this.editorcore.getAllAncestors();
25935         if (this.formatCombo) {
25936             
25937             
25938             var store = this.formatCombo.store;
25939             this.formatCombo.setValue("");
25940             for (var i =0; i < ans.length;i++) {
25941                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25942                     // select it..
25943                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25944                     break;
25945                 }
25946             }
25947         }
25948         
25949         
25950         
25951         // hides menus... - so this cant be on a menu...
25952         Roo.bootstrap.MenuMgr.hideAll();
25953         */
25954         Roo.bootstrap.MenuMgr.hideAll();
25955         //this.editorsyncValue();
25956     },
25957     onFirstFocus: function() {
25958         this.buttons.each(function(item){
25959            item.enable();
25960         });
25961     },
25962     toggleSourceEdit : function(sourceEditMode){
25963         
25964           
25965         if(sourceEditMode){
25966             Roo.log("disabling buttons");
25967            this.buttons.each( function(item){
25968                 if(item.cmd != 'pencil'){
25969                     item.disable();
25970                 }
25971             });
25972           
25973         }else{
25974             Roo.log("enabling buttons");
25975             if(this.editorcore.initialized){
25976                 this.buttons.each( function(item){
25977                     item.enable();
25978                 });
25979             }
25980             
25981         }
25982         Roo.log("calling toggole on editor");
25983         // tell the editor that it's been pressed..
25984         this.editor.toggleSourceEdit(sourceEditMode);
25985        
25986     }
25987 });
25988
25989
25990
25991
25992
25993 /**
25994  * @class Roo.bootstrap.Table.AbstractSelectionModel
25995  * @extends Roo.util.Observable
25996  * Abstract base class for grid SelectionModels.  It provides the interface that should be
25997  * implemented by descendant classes.  This class should not be directly instantiated.
25998  * @constructor
25999  */
26000 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26001     this.locked = false;
26002     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26003 };
26004
26005
26006 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26007     /** @ignore Called by the grid automatically. Do not call directly. */
26008     init : function(grid){
26009         this.grid = grid;
26010         this.initEvents();
26011     },
26012
26013     /**
26014      * Locks the selections.
26015      */
26016     lock : function(){
26017         this.locked = true;
26018     },
26019
26020     /**
26021      * Unlocks the selections.
26022      */
26023     unlock : function(){
26024         this.locked = false;
26025     },
26026
26027     /**
26028      * Returns true if the selections are locked.
26029      * @return {Boolean}
26030      */
26031     isLocked : function(){
26032         return this.locked;
26033     },
26034     
26035     
26036     initEvents : function ()
26037     {
26038         
26039     }
26040 });
26041 /**
26042  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26043  * @class Roo.bootstrap.Table.RowSelectionModel
26044  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26045  * It supports multiple selections and keyboard selection/navigation. 
26046  * @constructor
26047  * @param {Object} config
26048  */
26049
26050 Roo.bootstrap.Table.RowSelectionModel = function(config){
26051     Roo.apply(this, config);
26052     this.selections = new Roo.util.MixedCollection(false, function(o){
26053         return o.id;
26054     });
26055
26056     this.last = false;
26057     this.lastActive = false;
26058
26059     this.addEvents({
26060         /**
26061              * @event selectionchange
26062              * Fires when the selection changes
26063              * @param {SelectionModel} this
26064              */
26065             "selectionchange" : true,
26066         /**
26067              * @event afterselectionchange
26068              * Fires after the selection changes (eg. by key press or clicking)
26069              * @param {SelectionModel} this
26070              */
26071             "afterselectionchange" : true,
26072         /**
26073              * @event beforerowselect
26074              * Fires when a row is selected being selected, return false to cancel.
26075              * @param {SelectionModel} this
26076              * @param {Number} rowIndex The selected index
26077              * @param {Boolean} keepExisting False if other selections will be cleared
26078              */
26079             "beforerowselect" : true,
26080         /**
26081              * @event rowselect
26082              * Fires when a row is selected.
26083              * @param {SelectionModel} this
26084              * @param {Number} rowIndex The selected index
26085              * @param {Roo.data.Record} r The record
26086              */
26087             "rowselect" : true,
26088         /**
26089              * @event rowdeselect
26090              * Fires when a row is deselected.
26091              * @param {SelectionModel} this
26092              * @param {Number} rowIndex The selected index
26093              */
26094         "rowdeselect" : true
26095     });
26096     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26097     this.locked = false;
26098  };
26099
26100 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26101     /**
26102      * @cfg {Boolean} singleSelect
26103      * True to allow selection of only one row at a time (defaults to false)
26104      */
26105     singleSelect : false,
26106
26107     // private
26108     initEvents : function()
26109     {
26110
26111         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26112         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26113         //}else{ // allow click to work like normal
26114          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26115         //}
26116         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26117         this.grid.on("rowclick", this.handleMouseDown, this);
26118         
26119         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26120             "up" : function(e){
26121                 if(!e.shiftKey){
26122                     this.selectPrevious(e.shiftKey);
26123                 }else if(this.last !== false && this.lastActive !== false){
26124                     var last = this.last;
26125                     this.selectRange(this.last,  this.lastActive-1);
26126                     this.grid.getView().focusRow(this.lastActive);
26127                     if(last !== false){
26128                         this.last = last;
26129                     }
26130                 }else{
26131                     this.selectFirstRow();
26132                 }
26133                 this.fireEvent("afterselectionchange", this);
26134             },
26135             "down" : function(e){
26136                 if(!e.shiftKey){
26137                     this.selectNext(e.shiftKey);
26138                 }else if(this.last !== false && this.lastActive !== false){
26139                     var last = this.last;
26140                     this.selectRange(this.last,  this.lastActive+1);
26141                     this.grid.getView().focusRow(this.lastActive);
26142                     if(last !== false){
26143                         this.last = last;
26144                     }
26145                 }else{
26146                     this.selectFirstRow();
26147                 }
26148                 this.fireEvent("afterselectionchange", this);
26149             },
26150             scope: this
26151         });
26152         this.grid.store.on('load', function(){
26153             this.selections.clear();
26154         },this);
26155         /*
26156         var view = this.grid.view;
26157         view.on("refresh", this.onRefresh, this);
26158         view.on("rowupdated", this.onRowUpdated, this);
26159         view.on("rowremoved", this.onRemove, this);
26160         */
26161     },
26162
26163     // private
26164     onRefresh : function()
26165     {
26166         var ds = this.grid.store, i, v = this.grid.view;
26167         var s = this.selections;
26168         s.each(function(r){
26169             if((i = ds.indexOfId(r.id)) != -1){
26170                 v.onRowSelect(i);
26171             }else{
26172                 s.remove(r);
26173             }
26174         });
26175     },
26176
26177     // private
26178     onRemove : function(v, index, r){
26179         this.selections.remove(r);
26180     },
26181
26182     // private
26183     onRowUpdated : function(v, index, r){
26184         if(this.isSelected(r)){
26185             v.onRowSelect(index);
26186         }
26187     },
26188
26189     /**
26190      * Select records.
26191      * @param {Array} records The records to select
26192      * @param {Boolean} keepExisting (optional) True to keep existing selections
26193      */
26194     selectRecords : function(records, keepExisting)
26195     {
26196         if(!keepExisting){
26197             this.clearSelections();
26198         }
26199             var ds = this.grid.store;
26200         for(var i = 0, len = records.length; i < len; i++){
26201             this.selectRow(ds.indexOf(records[i]), true);
26202         }
26203     },
26204
26205     /**
26206      * Gets the number of selected rows.
26207      * @return {Number}
26208      */
26209     getCount : function(){
26210         return this.selections.length;
26211     },
26212
26213     /**
26214      * Selects the first row in the grid.
26215      */
26216     selectFirstRow : function(){
26217         this.selectRow(0);
26218     },
26219
26220     /**
26221      * Select the last row.
26222      * @param {Boolean} keepExisting (optional) True to keep existing selections
26223      */
26224     selectLastRow : function(keepExisting){
26225         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26226         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26227     },
26228
26229     /**
26230      * Selects the row immediately following the last selected row.
26231      * @param {Boolean} keepExisting (optional) True to keep existing selections
26232      */
26233     selectNext : function(keepExisting)
26234     {
26235             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26236             this.selectRow(this.last+1, keepExisting);
26237             this.grid.getView().focusRow(this.last);
26238         }
26239     },
26240
26241     /**
26242      * Selects the row that precedes the last selected row.
26243      * @param {Boolean} keepExisting (optional) True to keep existing selections
26244      */
26245     selectPrevious : function(keepExisting){
26246         if(this.last){
26247             this.selectRow(this.last-1, keepExisting);
26248             this.grid.getView().focusRow(this.last);
26249         }
26250     },
26251
26252     /**
26253      * Returns the selected records
26254      * @return {Array} Array of selected records
26255      */
26256     getSelections : function(){
26257         return [].concat(this.selections.items);
26258     },
26259
26260     /**
26261      * Returns the first selected record.
26262      * @return {Record}
26263      */
26264     getSelected : function(){
26265         return this.selections.itemAt(0);
26266     },
26267
26268
26269     /**
26270      * Clears all selections.
26271      */
26272     clearSelections : function(fast)
26273     {
26274         if(this.locked) {
26275             return;
26276         }
26277         if(fast !== true){
26278                 var ds = this.grid.store;
26279             var s = this.selections;
26280             s.each(function(r){
26281                 this.deselectRow(ds.indexOfId(r.id));
26282             }, this);
26283             s.clear();
26284         }else{
26285             this.selections.clear();
26286         }
26287         this.last = false;
26288     },
26289
26290
26291     /**
26292      * Selects all rows.
26293      */
26294     selectAll : function(){
26295         if(this.locked) {
26296             return;
26297         }
26298         this.selections.clear();
26299         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26300             this.selectRow(i, true);
26301         }
26302     },
26303
26304     /**
26305      * Returns True if there is a selection.
26306      * @return {Boolean}
26307      */
26308     hasSelection : function(){
26309         return this.selections.length > 0;
26310     },
26311
26312     /**
26313      * Returns True if the specified row is selected.
26314      * @param {Number/Record} record The record or index of the record to check
26315      * @return {Boolean}
26316      */
26317     isSelected : function(index){
26318             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26319         return (r && this.selections.key(r.id) ? true : false);
26320     },
26321
26322     /**
26323      * Returns True if the specified record id is selected.
26324      * @param {String} id The id of record to check
26325      * @return {Boolean}
26326      */
26327     isIdSelected : function(id){
26328         return (this.selections.key(id) ? true : false);
26329     },
26330
26331
26332     // private
26333     handleMouseDBClick : function(e, t){
26334         
26335     },
26336     // private
26337     handleMouseDown : function(e, t)
26338     {
26339             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26340         if(this.isLocked() || rowIndex < 0 ){
26341             return;
26342         };
26343         if(e.shiftKey && this.last !== false){
26344             var last = this.last;
26345             this.selectRange(last, rowIndex, e.ctrlKey);
26346             this.last = last; // reset the last
26347             t.focus();
26348     
26349         }else{
26350             var isSelected = this.isSelected(rowIndex);
26351             //Roo.log("select row:" + rowIndex);
26352             if(isSelected){
26353                 this.deselectRow(rowIndex);
26354             } else {
26355                         this.selectRow(rowIndex, true);
26356             }
26357     
26358             /*
26359                 if(e.button !== 0 && isSelected){
26360                 alert('rowIndex 2: ' + rowIndex);
26361                     view.focusRow(rowIndex);
26362                 }else if(e.ctrlKey && isSelected){
26363                     this.deselectRow(rowIndex);
26364                 }else if(!isSelected){
26365                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26366                     view.focusRow(rowIndex);
26367                 }
26368             */
26369         }
26370         this.fireEvent("afterselectionchange", this);
26371     },
26372     // private
26373     handleDragableRowClick :  function(grid, rowIndex, e) 
26374     {
26375         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26376             this.selectRow(rowIndex, false);
26377             grid.view.focusRow(rowIndex);
26378              this.fireEvent("afterselectionchange", this);
26379         }
26380     },
26381     
26382     /**
26383      * Selects multiple rows.
26384      * @param {Array} rows Array of the indexes of the row to select
26385      * @param {Boolean} keepExisting (optional) True to keep existing selections
26386      */
26387     selectRows : function(rows, keepExisting){
26388         if(!keepExisting){
26389             this.clearSelections();
26390         }
26391         for(var i = 0, len = rows.length; i < len; i++){
26392             this.selectRow(rows[i], true);
26393         }
26394     },
26395
26396     /**
26397      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26398      * @param {Number} startRow The index of the first row in the range
26399      * @param {Number} endRow The index of the last row in the range
26400      * @param {Boolean} keepExisting (optional) True to retain existing selections
26401      */
26402     selectRange : function(startRow, endRow, keepExisting){
26403         if(this.locked) {
26404             return;
26405         }
26406         if(!keepExisting){
26407             this.clearSelections();
26408         }
26409         if(startRow <= endRow){
26410             for(var i = startRow; i <= endRow; i++){
26411                 this.selectRow(i, true);
26412             }
26413         }else{
26414             for(var i = startRow; i >= endRow; i--){
26415                 this.selectRow(i, true);
26416             }
26417         }
26418     },
26419
26420     /**
26421      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26422      * @param {Number} startRow The index of the first row in the range
26423      * @param {Number} endRow The index of the last row in the range
26424      */
26425     deselectRange : function(startRow, endRow, preventViewNotify){
26426         if(this.locked) {
26427             return;
26428         }
26429         for(var i = startRow; i <= endRow; i++){
26430             this.deselectRow(i, preventViewNotify);
26431         }
26432     },
26433
26434     /**
26435      * Selects a row.
26436      * @param {Number} row The index of the row to select
26437      * @param {Boolean} keepExisting (optional) True to keep existing selections
26438      */
26439     selectRow : function(index, keepExisting, preventViewNotify)
26440     {
26441             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26442             return;
26443         }
26444         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26445             if(!keepExisting || this.singleSelect){
26446                 this.clearSelections();
26447             }
26448             
26449             var r = this.grid.store.getAt(index);
26450             //console.log('selectRow - record id :' + r.id);
26451             
26452             this.selections.add(r);
26453             this.last = this.lastActive = index;
26454             if(!preventViewNotify){
26455                 var proxy = new Roo.Element(
26456                                 this.grid.getRowDom(index)
26457                 );
26458                 proxy.addClass('bg-info info');
26459             }
26460             this.fireEvent("rowselect", this, index, r);
26461             this.fireEvent("selectionchange", this);
26462         }
26463     },
26464
26465     /**
26466      * Deselects a row.
26467      * @param {Number} row The index of the row to deselect
26468      */
26469     deselectRow : function(index, preventViewNotify)
26470     {
26471         if(this.locked) {
26472             return;
26473         }
26474         if(this.last == index){
26475             this.last = false;
26476         }
26477         if(this.lastActive == index){
26478             this.lastActive = false;
26479         }
26480         
26481         var r = this.grid.store.getAt(index);
26482         if (!r) {
26483             return;
26484         }
26485         
26486         this.selections.remove(r);
26487         //.console.log('deselectRow - record id :' + r.id);
26488         if(!preventViewNotify){
26489         
26490             var proxy = new Roo.Element(
26491                 this.grid.getRowDom(index)
26492             );
26493             proxy.removeClass('bg-info info');
26494         }
26495         this.fireEvent("rowdeselect", this, index);
26496         this.fireEvent("selectionchange", this);
26497     },
26498
26499     // private
26500     restoreLast : function(){
26501         if(this._last){
26502             this.last = this._last;
26503         }
26504     },
26505
26506     // private
26507     acceptsNav : function(row, col, cm){
26508         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26509     },
26510
26511     // private
26512     onEditorKey : function(field, e){
26513         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26514         if(k == e.TAB){
26515             e.stopEvent();
26516             ed.completeEdit();
26517             if(e.shiftKey){
26518                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26519             }else{
26520                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26521             }
26522         }else if(k == e.ENTER && !e.ctrlKey){
26523             e.stopEvent();
26524             ed.completeEdit();
26525             if(e.shiftKey){
26526                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26527             }else{
26528                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26529             }
26530         }else if(k == e.ESC){
26531             ed.cancelEdit();
26532         }
26533         if(newCell){
26534             g.startEditing(newCell[0], newCell[1]);
26535         }
26536     }
26537 });
26538 /*
26539  * Based on:
26540  * Ext JS Library 1.1.1
26541  * Copyright(c) 2006-2007, Ext JS, LLC.
26542  *
26543  * Originally Released Under LGPL - original licence link has changed is not relivant.
26544  *
26545  * Fork - LGPL
26546  * <script type="text/javascript">
26547  */
26548  
26549 /**
26550  * @class Roo.bootstrap.PagingToolbar
26551  * @extends Roo.bootstrap.NavSimplebar
26552  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26553  * @constructor
26554  * Create a new PagingToolbar
26555  * @param {Object} config The config object
26556  * @param {Roo.data.Store} store
26557  */
26558 Roo.bootstrap.PagingToolbar = function(config)
26559 {
26560     // old args format still supported... - xtype is prefered..
26561         // created from xtype...
26562     
26563     this.ds = config.dataSource;
26564     
26565     if (config.store && !this.ds) {
26566         this.store= Roo.factory(config.store, Roo.data);
26567         this.ds = this.store;
26568         this.ds.xmodule = this.xmodule || false;
26569     }
26570     
26571     this.toolbarItems = [];
26572     if (config.items) {
26573         this.toolbarItems = config.items;
26574     }
26575     
26576     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26577     
26578     this.cursor = 0;
26579     
26580     if (this.ds) { 
26581         this.bind(this.ds);
26582     }
26583     
26584     if (Roo.bootstrap.version == 4) {
26585         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26586     } else {
26587         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26588     }
26589     
26590 };
26591
26592 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26593     /**
26594      * @cfg {Roo.data.Store} dataSource
26595      * The underlying data store providing the paged data
26596      */
26597     /**
26598      * @cfg {String/HTMLElement/Element} container
26599      * container The id or element that will contain the toolbar
26600      */
26601     /**
26602      * @cfg {Boolean} displayInfo
26603      * True to display the displayMsg (defaults to false)
26604      */
26605     /**
26606      * @cfg {Number} pageSize
26607      * The number of records to display per page (defaults to 20)
26608      */
26609     pageSize: 20,
26610     /**
26611      * @cfg {String} displayMsg
26612      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26613      */
26614     displayMsg : 'Displaying {0} - {1} of {2}',
26615     /**
26616      * @cfg {String} emptyMsg
26617      * The message to display when no records are found (defaults to "No data to display")
26618      */
26619     emptyMsg : 'No data to display',
26620     /**
26621      * Customizable piece of the default paging text (defaults to "Page")
26622      * @type String
26623      */
26624     beforePageText : "Page",
26625     /**
26626      * Customizable piece of the default paging text (defaults to "of %0")
26627      * @type String
26628      */
26629     afterPageText : "of {0}",
26630     /**
26631      * Customizable piece of the default paging text (defaults to "First Page")
26632      * @type String
26633      */
26634     firstText : "First Page",
26635     /**
26636      * Customizable piece of the default paging text (defaults to "Previous Page")
26637      * @type String
26638      */
26639     prevText : "Previous Page",
26640     /**
26641      * Customizable piece of the default paging text (defaults to "Next Page")
26642      * @type String
26643      */
26644     nextText : "Next Page",
26645     /**
26646      * Customizable piece of the default paging text (defaults to "Last Page")
26647      * @type String
26648      */
26649     lastText : "Last Page",
26650     /**
26651      * Customizable piece of the default paging text (defaults to "Refresh")
26652      * @type String
26653      */
26654     refreshText : "Refresh",
26655
26656     buttons : false,
26657     // private
26658     onRender : function(ct, position) 
26659     {
26660         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26661         this.navgroup.parentId = this.id;
26662         this.navgroup.onRender(this.el, null);
26663         // add the buttons to the navgroup
26664         
26665         if(this.displayInfo){
26666             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26667             this.displayEl = this.el.select('.x-paging-info', true).first();
26668 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26669 //            this.displayEl = navel.el.select('span',true).first();
26670         }
26671         
26672         var _this = this;
26673         
26674         if(this.buttons){
26675             Roo.each(_this.buttons, function(e){ // this might need to use render????
26676                Roo.factory(e).render(_this.el);
26677             });
26678         }
26679             
26680         Roo.each(_this.toolbarItems, function(e) {
26681             _this.navgroup.addItem(e);
26682         });
26683         
26684         
26685         this.first = this.navgroup.addItem({
26686             tooltip: this.firstText,
26687             cls: "prev btn-outline-secondary",
26688             html : ' <i class="fa fa-step-backward"></i>',
26689             disabled: true,
26690             preventDefault: true,
26691             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26692         });
26693         
26694         this.prev =  this.navgroup.addItem({
26695             tooltip: this.prevText,
26696             cls: "prev btn-outline-secondary",
26697             html : ' <i class="fa fa-backward"></i>',
26698             disabled: true,
26699             preventDefault: true,
26700             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
26701         });
26702     //this.addSeparator();
26703         
26704         
26705         var field = this.navgroup.addItem( {
26706             tagtype : 'span',
26707             cls : 'x-paging-position  btn-outline-secondary',
26708              disabled: true,
26709             html : this.beforePageText  +
26710                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26711                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
26712          } ); //?? escaped?
26713         
26714         this.field = field.el.select('input', true).first();
26715         this.field.on("keydown", this.onPagingKeydown, this);
26716         this.field.on("focus", function(){this.dom.select();});
26717     
26718     
26719         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
26720         //this.field.setHeight(18);
26721         //this.addSeparator();
26722         this.next = this.navgroup.addItem({
26723             tooltip: this.nextText,
26724             cls: "next btn-outline-secondary",
26725             html : ' <i class="fa fa-forward"></i>',
26726             disabled: true,
26727             preventDefault: true,
26728             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
26729         });
26730         this.last = this.navgroup.addItem({
26731             tooltip: this.lastText,
26732             html : ' <i class="fa fa-step-forward"></i>',
26733             cls: "next btn-outline-secondary",
26734             disabled: true,
26735             preventDefault: true,
26736             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
26737         });
26738     //this.addSeparator();
26739         this.loading = this.navgroup.addItem({
26740             tooltip: this.refreshText,
26741             cls: "btn-outline-secondary",
26742             html : ' <i class="fa fa-refresh"></i>',
26743             preventDefault: true,
26744             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26745         });
26746         
26747     },
26748
26749     // private
26750     updateInfo : function(){
26751         if(this.displayEl){
26752             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26753             var msg = count == 0 ?
26754                 this.emptyMsg :
26755                 String.format(
26756                     this.displayMsg,
26757                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
26758                 );
26759             this.displayEl.update(msg);
26760         }
26761     },
26762
26763     // private
26764     onLoad : function(ds, r, o)
26765     {
26766         this.cursor = o.params.start ? o.params.start : 0;
26767         
26768         var d = this.getPageData(),
26769             ap = d.activePage,
26770             ps = d.pages;
26771         
26772         
26773         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26774         this.field.dom.value = ap;
26775         this.first.setDisabled(ap == 1);
26776         this.prev.setDisabled(ap == 1);
26777         this.next.setDisabled(ap == ps);
26778         this.last.setDisabled(ap == ps);
26779         this.loading.enable();
26780         this.updateInfo();
26781     },
26782
26783     // private
26784     getPageData : function(){
26785         var total = this.ds.getTotalCount();
26786         return {
26787             total : total,
26788             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26789             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
26790         };
26791     },
26792
26793     // private
26794     onLoadError : function(){
26795         this.loading.enable();
26796     },
26797
26798     // private
26799     onPagingKeydown : function(e){
26800         var k = e.getKey();
26801         var d = this.getPageData();
26802         if(k == e.RETURN){
26803             var v = this.field.dom.value, pageNum;
26804             if(!v || isNaN(pageNum = parseInt(v, 10))){
26805                 this.field.dom.value = d.activePage;
26806                 return;
26807             }
26808             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
26809             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26810             e.stopEvent();
26811         }
26812         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
26813         {
26814           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
26815           this.field.dom.value = pageNum;
26816           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
26817           e.stopEvent();
26818         }
26819         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
26820         {
26821           var v = this.field.dom.value, pageNum; 
26822           var increment = (e.shiftKey) ? 10 : 1;
26823           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
26824                 increment *= -1;
26825           }
26826           if(!v || isNaN(pageNum = parseInt(v, 10))) {
26827             this.field.dom.value = d.activePage;
26828             return;
26829           }
26830           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
26831           {
26832             this.field.dom.value = parseInt(v, 10) + increment;
26833             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
26834             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26835           }
26836           e.stopEvent();
26837         }
26838     },
26839
26840     // private
26841     beforeLoad : function(){
26842         if(this.loading){
26843             this.loading.disable();
26844         }
26845     },
26846
26847     // private
26848     onClick : function(which){
26849         
26850         var ds = this.ds;
26851         if (!ds) {
26852             return;
26853         }
26854         
26855         switch(which){
26856             case "first":
26857                 ds.load({params:{start: 0, limit: this.pageSize}});
26858             break;
26859             case "prev":
26860                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
26861             break;
26862             case "next":
26863                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
26864             break;
26865             case "last":
26866                 var total = ds.getTotalCount();
26867                 var extra = total % this.pageSize;
26868                 var lastStart = extra ? (total - extra) : total-this.pageSize;
26869                 ds.load({params:{start: lastStart, limit: this.pageSize}});
26870             break;
26871             case "refresh":
26872                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
26873             break;
26874         }
26875     },
26876
26877     /**
26878      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
26879      * @param {Roo.data.Store} store The data store to unbind
26880      */
26881     unbind : function(ds){
26882         ds.un("beforeload", this.beforeLoad, this);
26883         ds.un("load", this.onLoad, this);
26884         ds.un("loadexception", this.onLoadError, this);
26885         ds.un("remove", this.updateInfo, this);
26886         ds.un("add", this.updateInfo, this);
26887         this.ds = undefined;
26888     },
26889
26890     /**
26891      * Binds the paging toolbar to the specified {@link Roo.data.Store}
26892      * @param {Roo.data.Store} store The data store to bind
26893      */
26894     bind : function(ds){
26895         ds.on("beforeload", this.beforeLoad, this);
26896         ds.on("load", this.onLoad, this);
26897         ds.on("loadexception", this.onLoadError, this);
26898         ds.on("remove", this.updateInfo, this);
26899         ds.on("add", this.updateInfo, this);
26900         this.ds = ds;
26901     }
26902 });/*
26903  * - LGPL
26904  *
26905  * element
26906  * 
26907  */
26908
26909 /**
26910  * @class Roo.bootstrap.MessageBar
26911  * @extends Roo.bootstrap.Component
26912  * Bootstrap MessageBar class
26913  * @cfg {String} html contents of the MessageBar
26914  * @cfg {String} weight (info | success | warning | danger) default info
26915  * @cfg {String} beforeClass insert the bar before the given class
26916  * @cfg {Boolean} closable (true | false) default false
26917  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
26918  * 
26919  * @constructor
26920  * Create a new Element
26921  * @param {Object} config The config object
26922  */
26923
26924 Roo.bootstrap.MessageBar = function(config){
26925     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
26926 };
26927
26928 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
26929     
26930     html: '',
26931     weight: 'info',
26932     closable: false,
26933     fixed: false,
26934     beforeClass: 'bootstrap-sticky-wrap',
26935     
26936     getAutoCreate : function(){
26937         
26938         var cfg = {
26939             tag: 'div',
26940             cls: 'alert alert-dismissable alert-' + this.weight,
26941             cn: [
26942                 {
26943                     tag: 'span',
26944                     cls: 'message',
26945                     html: this.html || ''
26946                 }
26947             ]
26948         };
26949         
26950         if(this.fixed){
26951             cfg.cls += ' alert-messages-fixed';
26952         }
26953         
26954         if(this.closable){
26955             cfg.cn.push({
26956                 tag: 'button',
26957                 cls: 'close',
26958                 html: 'x'
26959             });
26960         }
26961         
26962         return cfg;
26963     },
26964     
26965     onRender : function(ct, position)
26966     {
26967         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
26968         
26969         if(!this.el){
26970             var cfg = Roo.apply({},  this.getAutoCreate());
26971             cfg.id = Roo.id();
26972             
26973             if (this.cls) {
26974                 cfg.cls += ' ' + this.cls;
26975             }
26976             if (this.style) {
26977                 cfg.style = this.style;
26978             }
26979             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
26980             
26981             this.el.setVisibilityMode(Roo.Element.DISPLAY);
26982         }
26983         
26984         this.el.select('>button.close').on('click', this.hide, this);
26985         
26986     },
26987     
26988     show : function()
26989     {
26990         if (!this.rendered) {
26991             this.render();
26992         }
26993         
26994         this.el.show();
26995         
26996         this.fireEvent('show', this);
26997         
26998     },
26999     
27000     hide : function()
27001     {
27002         if (!this.rendered) {
27003             this.render();
27004         }
27005         
27006         this.el.hide();
27007         
27008         this.fireEvent('hide', this);
27009     },
27010     
27011     update : function()
27012     {
27013 //        var e = this.el.dom.firstChild;
27014 //        
27015 //        if(this.closable){
27016 //            e = e.nextSibling;
27017 //        }
27018 //        
27019 //        e.data = this.html || '';
27020
27021         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27022     }
27023    
27024 });
27025
27026  
27027
27028      /*
27029  * - LGPL
27030  *
27031  * Graph
27032  * 
27033  */
27034
27035
27036 /**
27037  * @class Roo.bootstrap.Graph
27038  * @extends Roo.bootstrap.Component
27039  * Bootstrap Graph class
27040 > Prameters
27041  -sm {number} sm 4
27042  -md {number} md 5
27043  @cfg {String} graphtype  bar | vbar | pie
27044  @cfg {number} g_x coodinator | centre x (pie)
27045  @cfg {number} g_y coodinator | centre y (pie)
27046  @cfg {number} g_r radius (pie)
27047  @cfg {number} g_height height of the chart (respected by all elements in the set)
27048  @cfg {number} g_width width of the chart (respected by all elements in the set)
27049  @cfg {Object} title The title of the chart
27050     
27051  -{Array}  values
27052  -opts (object) options for the chart 
27053      o {
27054      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27055      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27056      o vgutter (number)
27057      o colors (array) colors be used repeatedly to plot the bars. If multicolumn bar is used each sequence of bars with use a different color.
27058      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27059      o to
27060      o stretch (boolean)
27061      o }
27062  -opts (object) options for the pie
27063      o{
27064      o cut
27065      o startAngle (number)
27066      o endAngle (number)
27067      } 
27068  *
27069  * @constructor
27070  * Create a new Input
27071  * @param {Object} config The config object
27072  */
27073
27074 Roo.bootstrap.Graph = function(config){
27075     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27076     
27077     this.addEvents({
27078         // img events
27079         /**
27080          * @event click
27081          * The img click event for the img.
27082          * @param {Roo.EventObject} e
27083          */
27084         "click" : true
27085     });
27086 };
27087
27088 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27089     
27090     sm: 4,
27091     md: 5,
27092     graphtype: 'bar',
27093     g_height: 250,
27094     g_width: 400,
27095     g_x: 50,
27096     g_y: 50,
27097     g_r: 30,
27098     opts:{
27099         //g_colors: this.colors,
27100         g_type: 'soft',
27101         g_gutter: '20%'
27102
27103     },
27104     title : false,
27105
27106     getAutoCreate : function(){
27107         
27108         var cfg = {
27109             tag: 'div',
27110             html : null
27111         };
27112         
27113         
27114         return  cfg;
27115     },
27116
27117     onRender : function(ct,position){
27118         
27119         
27120         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27121         
27122         if (typeof(Raphael) == 'undefined') {
27123             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27124             return;
27125         }
27126         
27127         this.raphael = Raphael(this.el.dom);
27128         
27129                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27130                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27131                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27132                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27133                 /*
27134                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27135                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27136                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27137                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27138                 
27139                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27140                 r.barchart(330, 10, 300, 220, data1);
27141                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27142                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27143                 */
27144                 
27145                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27146                 // r.barchart(30, 30, 560, 250,  xdata, {
27147                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27148                 //     axis : "0 0 1 1",
27149                 //     axisxlabels :  xdata
27150                 //     //yvalues : cols,
27151                    
27152                 // });
27153 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27154 //        
27155 //        this.load(null,xdata,{
27156 //                axis : "0 0 1 1",
27157 //                axisxlabels :  xdata
27158 //                });
27159
27160     },
27161
27162     load : function(graphtype,xdata,opts)
27163     {
27164         this.raphael.clear();
27165         if(!graphtype) {
27166             graphtype = this.graphtype;
27167         }
27168         if(!opts){
27169             opts = this.opts;
27170         }
27171         var r = this.raphael,
27172             fin = function () {
27173                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27174             },
27175             fout = function () {
27176                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27177             },
27178             pfin = function() {
27179                 this.sector.stop();
27180                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27181
27182                 if (this.label) {
27183                     this.label[0].stop();
27184                     this.label[0].attr({ r: 7.5 });
27185                     this.label[1].attr({ "font-weight": 800 });
27186                 }
27187             },
27188             pfout = function() {
27189                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27190
27191                 if (this.label) {
27192                     this.label[0].animate({ r: 5 }, 500, "bounce");
27193                     this.label[1].attr({ "font-weight": 400 });
27194                 }
27195             };
27196
27197         switch(graphtype){
27198             case 'bar':
27199                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27200                 break;
27201             case 'hbar':
27202                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27203                 break;
27204             case 'pie':
27205 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27206 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27207 //            
27208                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27209                 
27210                 break;
27211
27212         }
27213         
27214         if(this.title){
27215             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27216         }
27217         
27218     },
27219     
27220     setTitle: function(o)
27221     {
27222         this.title = o;
27223     },
27224     
27225     initEvents: function() {
27226         
27227         if(!this.href){
27228             this.el.on('click', this.onClick, this);
27229         }
27230     },
27231     
27232     onClick : function(e)
27233     {
27234         Roo.log('img onclick');
27235         this.fireEvent('click', this, e);
27236     }
27237    
27238 });
27239
27240  
27241 /*
27242  * - LGPL
27243  *
27244  * numberBox
27245  * 
27246  */
27247 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27248
27249 /**
27250  * @class Roo.bootstrap.dash.NumberBox
27251  * @extends Roo.bootstrap.Component
27252  * Bootstrap NumberBox class
27253  * @cfg {String} headline Box headline
27254  * @cfg {String} content Box content
27255  * @cfg {String} icon Box icon
27256  * @cfg {String} footer Footer text
27257  * @cfg {String} fhref Footer href
27258  * 
27259  * @constructor
27260  * Create a new NumberBox
27261  * @param {Object} config The config object
27262  */
27263
27264
27265 Roo.bootstrap.dash.NumberBox = function(config){
27266     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27267     
27268 };
27269
27270 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27271     
27272     headline : '',
27273     content : '',
27274     icon : '',
27275     footer : '',
27276     fhref : '',
27277     ficon : '',
27278     
27279     getAutoCreate : function(){
27280         
27281         var cfg = {
27282             tag : 'div',
27283             cls : 'small-box ',
27284             cn : [
27285                 {
27286                     tag : 'div',
27287                     cls : 'inner',
27288                     cn :[
27289                         {
27290                             tag : 'h3',
27291                             cls : 'roo-headline',
27292                             html : this.headline
27293                         },
27294                         {
27295                             tag : 'p',
27296                             cls : 'roo-content',
27297                             html : this.content
27298                         }
27299                     ]
27300                 }
27301             ]
27302         };
27303         
27304         if(this.icon){
27305             cfg.cn.push({
27306                 tag : 'div',
27307                 cls : 'icon',
27308                 cn :[
27309                     {
27310                         tag : 'i',
27311                         cls : 'ion ' + this.icon
27312                     }
27313                 ]
27314             });
27315         }
27316         
27317         if(this.footer){
27318             var footer = {
27319                 tag : 'a',
27320                 cls : 'small-box-footer',
27321                 href : this.fhref || '#',
27322                 html : this.footer
27323             };
27324             
27325             cfg.cn.push(footer);
27326             
27327         }
27328         
27329         return  cfg;
27330     },
27331
27332     onRender : function(ct,position){
27333         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27334
27335
27336        
27337                 
27338     },
27339
27340     setHeadline: function (value)
27341     {
27342         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27343     },
27344     
27345     setFooter: function (value, href)
27346     {
27347         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27348         
27349         if(href){
27350             this.el.select('a.small-box-footer',true).first().attr('href', href);
27351         }
27352         
27353     },
27354
27355     setContent: function (value)
27356     {
27357         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27358     },
27359
27360     initEvents: function() 
27361     {   
27362         
27363     }
27364     
27365 });
27366
27367  
27368 /*
27369  * - LGPL
27370  *
27371  * TabBox
27372  * 
27373  */
27374 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27375
27376 /**
27377  * @class Roo.bootstrap.dash.TabBox
27378  * @extends Roo.bootstrap.Component
27379  * Bootstrap TabBox class
27380  * @cfg {String} title Title of the TabBox
27381  * @cfg {String} icon Icon of the TabBox
27382  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27383  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27384  * 
27385  * @constructor
27386  * Create a new TabBox
27387  * @param {Object} config The config object
27388  */
27389
27390
27391 Roo.bootstrap.dash.TabBox = function(config){
27392     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27393     this.addEvents({
27394         // raw events
27395         /**
27396          * @event addpane
27397          * When a pane is added
27398          * @param {Roo.bootstrap.dash.TabPane} pane
27399          */
27400         "addpane" : true,
27401         /**
27402          * @event activatepane
27403          * When a pane is activated
27404          * @param {Roo.bootstrap.dash.TabPane} pane
27405          */
27406         "activatepane" : true
27407         
27408          
27409     });
27410     
27411     this.panes = [];
27412 };
27413
27414 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27415
27416     title : '',
27417     icon : false,
27418     showtabs : true,
27419     tabScrollable : false,
27420     
27421     getChildContainer : function()
27422     {
27423         return this.el.select('.tab-content', true).first();
27424     },
27425     
27426     getAutoCreate : function(){
27427         
27428         var header = {
27429             tag: 'li',
27430             cls: 'pull-left header',
27431             html: this.title,
27432             cn : []
27433         };
27434         
27435         if(this.icon){
27436             header.cn.push({
27437                 tag: 'i',
27438                 cls: 'fa ' + this.icon
27439             });
27440         }
27441         
27442         var h = {
27443             tag: 'ul',
27444             cls: 'nav nav-tabs pull-right',
27445             cn: [
27446                 header
27447             ]
27448         };
27449         
27450         if(this.tabScrollable){
27451             h = {
27452                 tag: 'div',
27453                 cls: 'tab-header',
27454                 cn: [
27455                     {
27456                         tag: 'ul',
27457                         cls: 'nav nav-tabs pull-right',
27458                         cn: [
27459                             header
27460                         ]
27461                     }
27462                 ]
27463             };
27464         }
27465         
27466         var cfg = {
27467             tag: 'div',
27468             cls: 'nav-tabs-custom',
27469             cn: [
27470                 h,
27471                 {
27472                     tag: 'div',
27473                     cls: 'tab-content no-padding',
27474                     cn: []
27475                 }
27476             ]
27477         };
27478
27479         return  cfg;
27480     },
27481     initEvents : function()
27482     {
27483         //Roo.log('add add pane handler');
27484         this.on('addpane', this.onAddPane, this);
27485     },
27486      /**
27487      * Updates the box title
27488      * @param {String} html to set the title to.
27489      */
27490     setTitle : function(value)
27491     {
27492         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27493     },
27494     onAddPane : function(pane)
27495     {
27496         this.panes.push(pane);
27497         //Roo.log('addpane');
27498         //Roo.log(pane);
27499         // tabs are rendere left to right..
27500         if(!this.showtabs){
27501             return;
27502         }
27503         
27504         var ctr = this.el.select('.nav-tabs', true).first();
27505          
27506          
27507         var existing = ctr.select('.nav-tab',true);
27508         var qty = existing.getCount();;
27509         
27510         
27511         var tab = ctr.createChild({
27512             tag : 'li',
27513             cls : 'nav-tab' + (qty ? '' : ' active'),
27514             cn : [
27515                 {
27516                     tag : 'a',
27517                     href:'#',
27518                     html : pane.title
27519                 }
27520             ]
27521         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27522         pane.tab = tab;
27523         
27524         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27525         if (!qty) {
27526             pane.el.addClass('active');
27527         }
27528         
27529                 
27530     },
27531     onTabClick : function(ev,un,ob,pane)
27532     {
27533         //Roo.log('tab - prev default');
27534         ev.preventDefault();
27535         
27536         
27537         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27538         pane.tab.addClass('active');
27539         //Roo.log(pane.title);
27540         this.getChildContainer().select('.tab-pane',true).removeClass('active');
27541         // technically we should have a deactivate event.. but maybe add later.
27542         // and it should not de-activate the selected tab...
27543         this.fireEvent('activatepane', pane);
27544         pane.el.addClass('active');
27545         pane.fireEvent('activate');
27546         
27547         
27548     },
27549     
27550     getActivePane : function()
27551     {
27552         var r = false;
27553         Roo.each(this.panes, function(p) {
27554             if(p.el.hasClass('active')){
27555                 r = p;
27556                 return false;
27557             }
27558             
27559             return;
27560         });
27561         
27562         return r;
27563     }
27564     
27565     
27566 });
27567
27568  
27569 /*
27570  * - LGPL
27571  *
27572  * Tab pane
27573  * 
27574  */
27575 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27576 /**
27577  * @class Roo.bootstrap.TabPane
27578  * @extends Roo.bootstrap.Component
27579  * Bootstrap TabPane class
27580  * @cfg {Boolean} active (false | true) Default false
27581  * @cfg {String} title title of panel
27582
27583  * 
27584  * @constructor
27585  * Create a new TabPane
27586  * @param {Object} config The config object
27587  */
27588
27589 Roo.bootstrap.dash.TabPane = function(config){
27590     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27591     
27592     this.addEvents({
27593         // raw events
27594         /**
27595          * @event activate
27596          * When a pane is activated
27597          * @param {Roo.bootstrap.dash.TabPane} pane
27598          */
27599         "activate" : true
27600          
27601     });
27602 };
27603
27604 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
27605     
27606     active : false,
27607     title : '',
27608     
27609     // the tabBox that this is attached to.
27610     tab : false,
27611      
27612     getAutoCreate : function() 
27613     {
27614         var cfg = {
27615             tag: 'div',
27616             cls: 'tab-pane'
27617         };
27618         
27619         if(this.active){
27620             cfg.cls += ' active';
27621         }
27622         
27623         return cfg;
27624     },
27625     initEvents  : function()
27626     {
27627         //Roo.log('trigger add pane handler');
27628         this.parent().fireEvent('addpane', this)
27629     },
27630     
27631      /**
27632      * Updates the tab title 
27633      * @param {String} html to set the title to.
27634      */
27635     setTitle: function(str)
27636     {
27637         if (!this.tab) {
27638             return;
27639         }
27640         this.title = str;
27641         this.tab.select('a', true).first().dom.innerHTML = str;
27642         
27643     }
27644     
27645     
27646     
27647 });
27648
27649  
27650
27651
27652  /*
27653  * - LGPL
27654  *
27655  * menu
27656  * 
27657  */
27658 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27659
27660 /**
27661  * @class Roo.bootstrap.menu.Menu
27662  * @extends Roo.bootstrap.Component
27663  * Bootstrap Menu class - container for Menu
27664  * @cfg {String} html Text of the menu
27665  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27666  * @cfg {String} icon Font awesome icon
27667  * @cfg {String} pos Menu align to (top | bottom) default bottom
27668  * 
27669  * 
27670  * @constructor
27671  * Create a new Menu
27672  * @param {Object} config The config object
27673  */
27674
27675
27676 Roo.bootstrap.menu.Menu = function(config){
27677     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27678     
27679     this.addEvents({
27680         /**
27681          * @event beforeshow
27682          * Fires before this menu is displayed
27683          * @param {Roo.bootstrap.menu.Menu} this
27684          */
27685         beforeshow : true,
27686         /**
27687          * @event beforehide
27688          * Fires before this menu is hidden
27689          * @param {Roo.bootstrap.menu.Menu} this
27690          */
27691         beforehide : true,
27692         /**
27693          * @event show
27694          * Fires after this menu is displayed
27695          * @param {Roo.bootstrap.menu.Menu} this
27696          */
27697         show : true,
27698         /**
27699          * @event hide
27700          * Fires after this menu is hidden
27701          * @param {Roo.bootstrap.menu.Menu} this
27702          */
27703         hide : true,
27704         /**
27705          * @event click
27706          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27707          * @param {Roo.bootstrap.menu.Menu} this
27708          * @param {Roo.EventObject} e
27709          */
27710         click : true
27711     });
27712     
27713 };
27714
27715 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
27716     
27717     submenu : false,
27718     html : '',
27719     weight : 'default',
27720     icon : false,
27721     pos : 'bottom',
27722     
27723     
27724     getChildContainer : function() {
27725         if(this.isSubMenu){
27726             return this.el;
27727         }
27728         
27729         return this.el.select('ul.dropdown-menu', true).first();  
27730     },
27731     
27732     getAutoCreate : function()
27733     {
27734         var text = [
27735             {
27736                 tag : 'span',
27737                 cls : 'roo-menu-text',
27738                 html : this.html
27739             }
27740         ];
27741         
27742         if(this.icon){
27743             text.unshift({
27744                 tag : 'i',
27745                 cls : 'fa ' + this.icon
27746             })
27747         }
27748         
27749         
27750         var cfg = {
27751             tag : 'div',
27752             cls : 'btn-group',
27753             cn : [
27754                 {
27755                     tag : 'button',
27756                     cls : 'dropdown-button btn btn-' + this.weight,
27757                     cn : text
27758                 },
27759                 {
27760                     tag : 'button',
27761                     cls : 'dropdown-toggle btn btn-' + this.weight,
27762                     cn : [
27763                         {
27764                             tag : 'span',
27765                             cls : 'caret'
27766                         }
27767                     ]
27768                 },
27769                 {
27770                     tag : 'ul',
27771                     cls : 'dropdown-menu'
27772                 }
27773             ]
27774             
27775         };
27776         
27777         if(this.pos == 'top'){
27778             cfg.cls += ' dropup';
27779         }
27780         
27781         if(this.isSubMenu){
27782             cfg = {
27783                 tag : 'ul',
27784                 cls : 'dropdown-menu'
27785             }
27786         }
27787         
27788         return cfg;
27789     },
27790     
27791     onRender : function(ct, position)
27792     {
27793         this.isSubMenu = ct.hasClass('dropdown-submenu');
27794         
27795         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
27796     },
27797     
27798     initEvents : function() 
27799     {
27800         if(this.isSubMenu){
27801             return;
27802         }
27803         
27804         this.hidden = true;
27805         
27806         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
27807         this.triggerEl.on('click', this.onTriggerPress, this);
27808         
27809         this.buttonEl = this.el.select('button.dropdown-button', true).first();
27810         this.buttonEl.on('click', this.onClick, this);
27811         
27812     },
27813     
27814     list : function()
27815     {
27816         if(this.isSubMenu){
27817             return this.el;
27818         }
27819         
27820         return this.el.select('ul.dropdown-menu', true).first();
27821     },
27822     
27823     onClick : function(e)
27824     {
27825         this.fireEvent("click", this, e);
27826     },
27827     
27828     onTriggerPress  : function(e)
27829     {   
27830         if (this.isVisible()) {
27831             this.hide();
27832         } else {
27833             this.show();
27834         }
27835     },
27836     
27837     isVisible : function(){
27838         return !this.hidden;
27839     },
27840     
27841     show : function()
27842     {
27843         this.fireEvent("beforeshow", this);
27844         
27845         this.hidden = false;
27846         this.el.addClass('open');
27847         
27848         Roo.get(document).on("mouseup", this.onMouseUp, this);
27849         
27850         this.fireEvent("show", this);
27851         
27852         
27853     },
27854     
27855     hide : function()
27856     {
27857         this.fireEvent("beforehide", this);
27858         
27859         this.hidden = true;
27860         this.el.removeClass('open');
27861         
27862         Roo.get(document).un("mouseup", this.onMouseUp);
27863         
27864         this.fireEvent("hide", this);
27865     },
27866     
27867     onMouseUp : function()
27868     {
27869         this.hide();
27870     }
27871     
27872 });
27873
27874  
27875  /*
27876  * - LGPL
27877  *
27878  * menu item
27879  * 
27880  */
27881 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27882
27883 /**
27884  * @class Roo.bootstrap.menu.Item
27885  * @extends Roo.bootstrap.Component
27886  * Bootstrap MenuItem class
27887  * @cfg {Boolean} submenu (true | false) default false
27888  * @cfg {String} html text of the item
27889  * @cfg {String} href the link
27890  * @cfg {Boolean} disable (true | false) default false
27891  * @cfg {Boolean} preventDefault (true | false) default true
27892  * @cfg {String} icon Font awesome icon
27893  * @cfg {String} pos Submenu align to (left | right) default right 
27894  * 
27895  * 
27896  * @constructor
27897  * Create a new Item
27898  * @param {Object} config The config object
27899  */
27900
27901
27902 Roo.bootstrap.menu.Item = function(config){
27903     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
27904     this.addEvents({
27905         /**
27906          * @event mouseover
27907          * Fires when the mouse is hovering over this menu
27908          * @param {Roo.bootstrap.menu.Item} this
27909          * @param {Roo.EventObject} e
27910          */
27911         mouseover : true,
27912         /**
27913          * @event mouseout
27914          * Fires when the mouse exits this menu
27915          * @param {Roo.bootstrap.menu.Item} this
27916          * @param {Roo.EventObject} e
27917          */
27918         mouseout : true,
27919         // raw events
27920         /**
27921          * @event click
27922          * The raw click event for the entire grid.
27923          * @param {Roo.EventObject} e
27924          */
27925         click : true
27926     });
27927 };
27928
27929 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
27930     
27931     submenu : false,
27932     href : '',
27933     html : '',
27934     preventDefault: true,
27935     disable : false,
27936     icon : false,
27937     pos : 'right',
27938     
27939     getAutoCreate : function()
27940     {
27941         var text = [
27942             {
27943                 tag : 'span',
27944                 cls : 'roo-menu-item-text',
27945                 html : this.html
27946             }
27947         ];
27948         
27949         if(this.icon){
27950             text.unshift({
27951                 tag : 'i',
27952                 cls : 'fa ' + this.icon
27953             })
27954         }
27955         
27956         var cfg = {
27957             tag : 'li',
27958             cn : [
27959                 {
27960                     tag : 'a',
27961                     href : this.href || '#',
27962                     cn : text
27963                 }
27964             ]
27965         };
27966         
27967         if(this.disable){
27968             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
27969         }
27970         
27971         if(this.submenu){
27972             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
27973             
27974             if(this.pos == 'left'){
27975                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
27976             }
27977         }
27978         
27979         return cfg;
27980     },
27981     
27982     initEvents : function() 
27983     {
27984         this.el.on('mouseover', this.onMouseOver, this);
27985         this.el.on('mouseout', this.onMouseOut, this);
27986         
27987         this.el.select('a', true).first().on('click', this.onClick, this);
27988         
27989     },
27990     
27991     onClick : function(e)
27992     {
27993         if(this.preventDefault){
27994             e.preventDefault();
27995         }
27996         
27997         this.fireEvent("click", this, e);
27998     },
27999     
28000     onMouseOver : function(e)
28001     {
28002         if(this.submenu && this.pos == 'left'){
28003             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28004         }
28005         
28006         this.fireEvent("mouseover", this, e);
28007     },
28008     
28009     onMouseOut : function(e)
28010     {
28011         this.fireEvent("mouseout", this, e);
28012     }
28013 });
28014
28015  
28016
28017  /*
28018  * - LGPL
28019  *
28020  * menu separator
28021  * 
28022  */
28023 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28024
28025 /**
28026  * @class Roo.bootstrap.menu.Separator
28027  * @extends Roo.bootstrap.Component
28028  * Bootstrap Separator class
28029  * 
28030  * @constructor
28031  * Create a new Separator
28032  * @param {Object} config The config object
28033  */
28034
28035
28036 Roo.bootstrap.menu.Separator = function(config){
28037     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28038 };
28039
28040 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28041     
28042     getAutoCreate : function(){
28043         var cfg = {
28044             tag : 'li',
28045             cls: 'divider'
28046         };
28047         
28048         return cfg;
28049     }
28050    
28051 });
28052
28053  
28054
28055  /*
28056  * - LGPL
28057  *
28058  * Tooltip
28059  * 
28060  */
28061
28062 /**
28063  * @class Roo.bootstrap.Tooltip
28064  * Bootstrap Tooltip class
28065  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28066  * to determine which dom element triggers the tooltip.
28067  * 
28068  * It needs to add support for additional attributes like tooltip-position
28069  * 
28070  * @constructor
28071  * Create a new Toolti
28072  * @param {Object} config The config object
28073  */
28074
28075 Roo.bootstrap.Tooltip = function(config){
28076     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28077     
28078     this.alignment = Roo.bootstrap.Tooltip.alignment;
28079     
28080     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28081         this.alignment = config.alignment;
28082     }
28083     
28084 };
28085
28086 Roo.apply(Roo.bootstrap.Tooltip, {
28087     /**
28088      * @function init initialize tooltip monitoring.
28089      * @static
28090      */
28091     currentEl : false,
28092     currentTip : false,
28093     currentRegion : false,
28094     
28095     //  init : delay?
28096     
28097     init : function()
28098     {
28099         Roo.get(document).on('mouseover', this.enter ,this);
28100         Roo.get(document).on('mouseout', this.leave, this);
28101          
28102         
28103         this.currentTip = new Roo.bootstrap.Tooltip();
28104     },
28105     
28106     enter : function(ev)
28107     {
28108         var dom = ev.getTarget();
28109         
28110         //Roo.log(['enter',dom]);
28111         var el = Roo.fly(dom);
28112         if (this.currentEl) {
28113             //Roo.log(dom);
28114             //Roo.log(this.currentEl);
28115             //Roo.log(this.currentEl.contains(dom));
28116             if (this.currentEl == el) {
28117                 return;
28118             }
28119             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28120                 return;
28121             }
28122
28123         }
28124         
28125         if (this.currentTip.el) {
28126             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28127         }    
28128         //Roo.log(ev);
28129         
28130         if(!el || el.dom == document){
28131             return;
28132         }
28133         
28134         var bindEl = el;
28135         
28136         // you can not look for children, as if el is the body.. then everythign is the child..
28137         if (!el.attr('tooltip')) { //
28138             if (!el.select("[tooltip]").elements.length) {
28139                 return;
28140             }
28141             // is the mouse over this child...?
28142             bindEl = el.select("[tooltip]").first();
28143             var xy = ev.getXY();
28144             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28145                 //Roo.log("not in region.");
28146                 return;
28147             }
28148             //Roo.log("child element over..");
28149             
28150         }
28151         this.currentEl = bindEl;
28152         this.currentTip.bind(bindEl);
28153         this.currentRegion = Roo.lib.Region.getRegion(dom);
28154         this.currentTip.enter();
28155         
28156     },
28157     leave : function(ev)
28158     {
28159         var dom = ev.getTarget();
28160         //Roo.log(['leave',dom]);
28161         if (!this.currentEl) {
28162             return;
28163         }
28164         
28165         
28166         if (dom != this.currentEl.dom) {
28167             return;
28168         }
28169         var xy = ev.getXY();
28170         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28171             return;
28172         }
28173         // only activate leave if mouse cursor is outside... bounding box..
28174         
28175         
28176         
28177         
28178         if (this.currentTip) {
28179             this.currentTip.leave();
28180         }
28181         //Roo.log('clear currentEl');
28182         this.currentEl = false;
28183         
28184         
28185     },
28186     alignment : {
28187         'left' : ['r-l', [-2,0], 'right'],
28188         'right' : ['l-r', [2,0], 'left'],
28189         'bottom' : ['t-b', [0,2], 'top'],
28190         'top' : [ 'b-t', [0,-2], 'bottom']
28191     }
28192     
28193 });
28194
28195
28196 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28197     
28198     
28199     bindEl : false,
28200     
28201     delay : null, // can be { show : 300 , hide: 500}
28202     
28203     timeout : null,
28204     
28205     hoverState : null, //???
28206     
28207     placement : 'bottom', 
28208     
28209     alignment : false,
28210     
28211     getAutoCreate : function(){
28212     
28213         var cfg = {
28214            cls : 'tooltip',
28215            role : 'tooltip',
28216            cn : [
28217                 {
28218                     cls : 'tooltip-arrow'
28219                 },
28220                 {
28221                     cls : 'tooltip-inner'
28222                 }
28223            ]
28224         };
28225         
28226         return cfg;
28227     },
28228     bind : function(el)
28229     {
28230         this.bindEl = el;
28231     },
28232       
28233     
28234     enter : function () {
28235        
28236         if (this.timeout != null) {
28237             clearTimeout(this.timeout);
28238         }
28239         
28240         this.hoverState = 'in';
28241          //Roo.log("enter - show");
28242         if (!this.delay || !this.delay.show) {
28243             this.show();
28244             return;
28245         }
28246         var _t = this;
28247         this.timeout = setTimeout(function () {
28248             if (_t.hoverState == 'in') {
28249                 _t.show();
28250             }
28251         }, this.delay.show);
28252     },
28253     leave : function()
28254     {
28255         clearTimeout(this.timeout);
28256     
28257         this.hoverState = 'out';
28258          if (!this.delay || !this.delay.hide) {
28259             this.hide();
28260             return;
28261         }
28262        
28263         var _t = this;
28264         this.timeout = setTimeout(function () {
28265             //Roo.log("leave - timeout");
28266             
28267             if (_t.hoverState == 'out') {
28268                 _t.hide();
28269                 Roo.bootstrap.Tooltip.currentEl = false;
28270             }
28271         }, delay);
28272     },
28273     
28274     show : function (msg)
28275     {
28276         if (!this.el) {
28277             this.render(document.body);
28278         }
28279         // set content.
28280         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28281         
28282         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28283         
28284         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28285         
28286         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
28287         
28288         var placement = typeof this.placement == 'function' ?
28289             this.placement.call(this, this.el, on_el) :
28290             this.placement;
28291             
28292         var autoToken = /\s?auto?\s?/i;
28293         var autoPlace = autoToken.test(placement);
28294         if (autoPlace) {
28295             placement = placement.replace(autoToken, '') || 'top';
28296         }
28297         
28298         //this.el.detach()
28299         //this.el.setXY([0,0]);
28300         this.el.show();
28301         //this.el.dom.style.display='block';
28302         
28303         //this.el.appendTo(on_el);
28304         
28305         var p = this.getPosition();
28306         var box = this.el.getBox();
28307         
28308         if (autoPlace) {
28309             // fixme..
28310         }
28311         
28312         var align = this.alignment[placement];
28313         
28314         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28315         
28316         if(placement == 'top' || placement == 'bottom'){
28317             if(xy[0] < 0){
28318                 placement = 'right';
28319             }
28320             
28321             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28322                 placement = 'left';
28323             }
28324             
28325             var scroll = Roo.select('body', true).first().getScroll();
28326             
28327             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28328                 placement = 'top';
28329             }
28330             
28331             align = this.alignment[placement];
28332         }
28333         
28334         this.el.alignTo(this.bindEl, align[0],align[1]);
28335         //var arrow = this.el.select('.arrow',true).first();
28336         //arrow.set(align[2], 
28337         
28338         this.el.addClass(placement);
28339         
28340         this.el.addClass('in fade');
28341         
28342         this.hoverState = null;
28343         
28344         if (this.el.hasClass('fade')) {
28345             // fade it?
28346         }
28347         
28348     },
28349     hide : function()
28350     {
28351          
28352         if (!this.el) {
28353             return;
28354         }
28355         //this.el.setXY([0,0]);
28356         this.el.removeClass('in');
28357         //this.el.hide();
28358         
28359     }
28360     
28361 });
28362  
28363
28364  /*
28365  * - LGPL
28366  *
28367  * Location Picker
28368  * 
28369  */
28370
28371 /**
28372  * @class Roo.bootstrap.LocationPicker
28373  * @extends Roo.bootstrap.Component
28374  * Bootstrap LocationPicker class
28375  * @cfg {Number} latitude Position when init default 0
28376  * @cfg {Number} longitude Position when init default 0
28377  * @cfg {Number} zoom default 15
28378  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28379  * @cfg {Boolean} mapTypeControl default false
28380  * @cfg {Boolean} disableDoubleClickZoom default false
28381  * @cfg {Boolean} scrollwheel default true
28382  * @cfg {Boolean} streetViewControl default false
28383  * @cfg {Number} radius default 0
28384  * @cfg {String} locationName
28385  * @cfg {Boolean} draggable default true
28386  * @cfg {Boolean} enableAutocomplete default false
28387  * @cfg {Boolean} enableReverseGeocode default true
28388  * @cfg {String} markerTitle
28389  * 
28390  * @constructor
28391  * Create a new LocationPicker
28392  * @param {Object} config The config object
28393  */
28394
28395
28396 Roo.bootstrap.LocationPicker = function(config){
28397     
28398     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28399     
28400     this.addEvents({
28401         /**
28402          * @event initial
28403          * Fires when the picker initialized.
28404          * @param {Roo.bootstrap.LocationPicker} this
28405          * @param {Google Location} location
28406          */
28407         initial : true,
28408         /**
28409          * @event positionchanged
28410          * Fires when the picker position changed.
28411          * @param {Roo.bootstrap.LocationPicker} this
28412          * @param {Google Location} location
28413          */
28414         positionchanged : true,
28415         /**
28416          * @event resize
28417          * Fires when the map resize.
28418          * @param {Roo.bootstrap.LocationPicker} this
28419          */
28420         resize : true,
28421         /**
28422          * @event show
28423          * Fires when the map show.
28424          * @param {Roo.bootstrap.LocationPicker} this
28425          */
28426         show : true,
28427         /**
28428          * @event hide
28429          * Fires when the map hide.
28430          * @param {Roo.bootstrap.LocationPicker} this
28431          */
28432         hide : true,
28433         /**
28434          * @event mapClick
28435          * Fires when click the map.
28436          * @param {Roo.bootstrap.LocationPicker} this
28437          * @param {Map event} e
28438          */
28439         mapClick : true,
28440         /**
28441          * @event mapRightClick
28442          * Fires when right click the map.
28443          * @param {Roo.bootstrap.LocationPicker} this
28444          * @param {Map event} e
28445          */
28446         mapRightClick : true,
28447         /**
28448          * @event markerClick
28449          * Fires when click the marker.
28450          * @param {Roo.bootstrap.LocationPicker} this
28451          * @param {Map event} e
28452          */
28453         markerClick : true,
28454         /**
28455          * @event markerRightClick
28456          * Fires when right click the marker.
28457          * @param {Roo.bootstrap.LocationPicker} this
28458          * @param {Map event} e
28459          */
28460         markerRightClick : true,
28461         /**
28462          * @event OverlayViewDraw
28463          * Fires when OverlayView Draw
28464          * @param {Roo.bootstrap.LocationPicker} this
28465          */
28466         OverlayViewDraw : true,
28467         /**
28468          * @event OverlayViewOnAdd
28469          * Fires when OverlayView Draw
28470          * @param {Roo.bootstrap.LocationPicker} this
28471          */
28472         OverlayViewOnAdd : true,
28473         /**
28474          * @event OverlayViewOnRemove
28475          * Fires when OverlayView Draw
28476          * @param {Roo.bootstrap.LocationPicker} this
28477          */
28478         OverlayViewOnRemove : true,
28479         /**
28480          * @event OverlayViewShow
28481          * Fires when OverlayView Draw
28482          * @param {Roo.bootstrap.LocationPicker} this
28483          * @param {Pixel} cpx
28484          */
28485         OverlayViewShow : true,
28486         /**
28487          * @event OverlayViewHide
28488          * Fires when OverlayView Draw
28489          * @param {Roo.bootstrap.LocationPicker} this
28490          */
28491         OverlayViewHide : true,
28492         /**
28493          * @event loadexception
28494          * Fires when load google lib failed.
28495          * @param {Roo.bootstrap.LocationPicker} this
28496          */
28497         loadexception : true
28498     });
28499         
28500 };
28501
28502 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
28503     
28504     gMapContext: false,
28505     
28506     latitude: 0,
28507     longitude: 0,
28508     zoom: 15,
28509     mapTypeId: false,
28510     mapTypeControl: false,
28511     disableDoubleClickZoom: false,
28512     scrollwheel: true,
28513     streetViewControl: false,
28514     radius: 0,
28515     locationName: '',
28516     draggable: true,
28517     enableAutocomplete: false,
28518     enableReverseGeocode: true,
28519     markerTitle: '',
28520     
28521     getAutoCreate: function()
28522     {
28523
28524         var cfg = {
28525             tag: 'div',
28526             cls: 'roo-location-picker'
28527         };
28528         
28529         return cfg
28530     },
28531     
28532     initEvents: function(ct, position)
28533     {       
28534         if(!this.el.getWidth() || this.isApplied()){
28535             return;
28536         }
28537         
28538         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28539         
28540         this.initial();
28541     },
28542     
28543     initial: function()
28544     {
28545         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28546             this.fireEvent('loadexception', this);
28547             return;
28548         }
28549         
28550         if(!this.mapTypeId){
28551             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28552         }
28553         
28554         this.gMapContext = this.GMapContext();
28555         
28556         this.initOverlayView();
28557         
28558         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28559         
28560         var _this = this;
28561                 
28562         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28563             _this.setPosition(_this.gMapContext.marker.position);
28564         });
28565         
28566         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28567             _this.fireEvent('mapClick', this, event);
28568             
28569         });
28570
28571         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28572             _this.fireEvent('mapRightClick', this, event);
28573             
28574         });
28575         
28576         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28577             _this.fireEvent('markerClick', this, event);
28578             
28579         });
28580
28581         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28582             _this.fireEvent('markerRightClick', this, event);
28583             
28584         });
28585         
28586         this.setPosition(this.gMapContext.location);
28587         
28588         this.fireEvent('initial', this, this.gMapContext.location);
28589     },
28590     
28591     initOverlayView: function()
28592     {
28593         var _this = this;
28594         
28595         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28596             
28597             draw: function()
28598             {
28599                 _this.fireEvent('OverlayViewDraw', _this);
28600             },
28601             
28602             onAdd: function()
28603             {
28604                 _this.fireEvent('OverlayViewOnAdd', _this);
28605             },
28606             
28607             onRemove: function()
28608             {
28609                 _this.fireEvent('OverlayViewOnRemove', _this);
28610             },
28611             
28612             show: function(cpx)
28613             {
28614                 _this.fireEvent('OverlayViewShow', _this, cpx);
28615             },
28616             
28617             hide: function()
28618             {
28619                 _this.fireEvent('OverlayViewHide', _this);
28620             }
28621             
28622         });
28623     },
28624     
28625     fromLatLngToContainerPixel: function(event)
28626     {
28627         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28628     },
28629     
28630     isApplied: function() 
28631     {
28632         return this.getGmapContext() == false ? false : true;
28633     },
28634     
28635     getGmapContext: function() 
28636     {
28637         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28638     },
28639     
28640     GMapContext: function() 
28641     {
28642         var position = new google.maps.LatLng(this.latitude, this.longitude);
28643         
28644         var _map = new google.maps.Map(this.el.dom, {
28645             center: position,
28646             zoom: this.zoom,
28647             mapTypeId: this.mapTypeId,
28648             mapTypeControl: this.mapTypeControl,
28649             disableDoubleClickZoom: this.disableDoubleClickZoom,
28650             scrollwheel: this.scrollwheel,
28651             streetViewControl: this.streetViewControl,
28652             locationName: this.locationName,
28653             draggable: this.draggable,
28654             enableAutocomplete: this.enableAutocomplete,
28655             enableReverseGeocode: this.enableReverseGeocode
28656         });
28657         
28658         var _marker = new google.maps.Marker({
28659             position: position,
28660             map: _map,
28661             title: this.markerTitle,
28662             draggable: this.draggable
28663         });
28664         
28665         return {
28666             map: _map,
28667             marker: _marker,
28668             circle: null,
28669             location: position,
28670             radius: this.radius,
28671             locationName: this.locationName,
28672             addressComponents: {
28673                 formatted_address: null,
28674                 addressLine1: null,
28675                 addressLine2: null,
28676                 streetName: null,
28677                 streetNumber: null,
28678                 city: null,
28679                 district: null,
28680                 state: null,
28681                 stateOrProvince: null
28682             },
28683             settings: this,
28684             domContainer: this.el.dom,
28685             geodecoder: new google.maps.Geocoder()
28686         };
28687     },
28688     
28689     drawCircle: function(center, radius, options) 
28690     {
28691         if (this.gMapContext.circle != null) {
28692             this.gMapContext.circle.setMap(null);
28693         }
28694         if (radius > 0) {
28695             radius *= 1;
28696             options = Roo.apply({}, options, {
28697                 strokeColor: "#0000FF",
28698                 strokeOpacity: .35,
28699                 strokeWeight: 2,
28700                 fillColor: "#0000FF",
28701                 fillOpacity: .2
28702             });
28703             
28704             options.map = this.gMapContext.map;
28705             options.radius = radius;
28706             options.center = center;
28707             this.gMapContext.circle = new google.maps.Circle(options);
28708             return this.gMapContext.circle;
28709         }
28710         
28711         return null;
28712     },
28713     
28714     setPosition: function(location) 
28715     {
28716         this.gMapContext.location = location;
28717         this.gMapContext.marker.setPosition(location);
28718         this.gMapContext.map.panTo(location);
28719         this.drawCircle(location, this.gMapContext.radius, {});
28720         
28721         var _this = this;
28722         
28723         if (this.gMapContext.settings.enableReverseGeocode) {
28724             this.gMapContext.geodecoder.geocode({
28725                 latLng: this.gMapContext.location
28726             }, function(results, status) {
28727                 
28728                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28729                     _this.gMapContext.locationName = results[0].formatted_address;
28730                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28731                     
28732                     _this.fireEvent('positionchanged', this, location);
28733                 }
28734             });
28735             
28736             return;
28737         }
28738         
28739         this.fireEvent('positionchanged', this, location);
28740     },
28741     
28742     resize: function()
28743     {
28744         google.maps.event.trigger(this.gMapContext.map, "resize");
28745         
28746         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28747         
28748         this.fireEvent('resize', this);
28749     },
28750     
28751     setPositionByLatLng: function(latitude, longitude)
28752     {
28753         this.setPosition(new google.maps.LatLng(latitude, longitude));
28754     },
28755     
28756     getCurrentPosition: function() 
28757     {
28758         return {
28759             latitude: this.gMapContext.location.lat(),
28760             longitude: this.gMapContext.location.lng()
28761         };
28762     },
28763     
28764     getAddressName: function() 
28765     {
28766         return this.gMapContext.locationName;
28767     },
28768     
28769     getAddressComponents: function() 
28770     {
28771         return this.gMapContext.addressComponents;
28772     },
28773     
28774     address_component_from_google_geocode: function(address_components) 
28775     {
28776         var result = {};
28777         
28778         for (var i = 0; i < address_components.length; i++) {
28779             var component = address_components[i];
28780             if (component.types.indexOf("postal_code") >= 0) {
28781                 result.postalCode = component.short_name;
28782             } else if (component.types.indexOf("street_number") >= 0) {
28783                 result.streetNumber = component.short_name;
28784             } else if (component.types.indexOf("route") >= 0) {
28785                 result.streetName = component.short_name;
28786             } else if (component.types.indexOf("neighborhood") >= 0) {
28787                 result.city = component.short_name;
28788             } else if (component.types.indexOf("locality") >= 0) {
28789                 result.city = component.short_name;
28790             } else if (component.types.indexOf("sublocality") >= 0) {
28791                 result.district = component.short_name;
28792             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
28793                 result.stateOrProvince = component.short_name;
28794             } else if (component.types.indexOf("country") >= 0) {
28795                 result.country = component.short_name;
28796             }
28797         }
28798         
28799         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
28800         result.addressLine2 = "";
28801         return result;
28802     },
28803     
28804     setZoomLevel: function(zoom)
28805     {
28806         this.gMapContext.map.setZoom(zoom);
28807     },
28808     
28809     show: function()
28810     {
28811         if(!this.el){
28812             return;
28813         }
28814         
28815         this.el.show();
28816         
28817         this.resize();
28818         
28819         this.fireEvent('show', this);
28820     },
28821     
28822     hide: function()
28823     {
28824         if(!this.el){
28825             return;
28826         }
28827         
28828         this.el.hide();
28829         
28830         this.fireEvent('hide', this);
28831     }
28832     
28833 });
28834
28835 Roo.apply(Roo.bootstrap.LocationPicker, {
28836     
28837     OverlayView : function(map, options)
28838     {
28839         options = options || {};
28840         
28841         this.setMap(map);
28842     }
28843     
28844     
28845 });/**
28846  * @class Roo.bootstrap.Alert
28847  * @extends Roo.bootstrap.Component
28848  * Bootstrap Alert class - shows an alert area box
28849  * eg
28850  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
28851   Enter a valid email address
28852 </div>
28853  * @licence LGPL
28854  * @cfg {String} title The title of alert
28855  * @cfg {String} html The content of alert
28856  * @cfg {String} weight (  success | info | warning | danger )
28857  * @cfg {String} faicon font-awesomeicon
28858  * 
28859  * @constructor
28860  * Create a new alert
28861  * @param {Object} config The config object
28862  */
28863
28864
28865 Roo.bootstrap.Alert = function(config){
28866     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
28867     
28868 };
28869
28870 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
28871     
28872     title: '',
28873     html: '',
28874     weight: false,
28875     faicon: false,
28876     
28877     getAutoCreate : function()
28878     {
28879         
28880         var cfg = {
28881             tag : 'div',
28882             cls : 'alert',
28883             cn : [
28884                 {
28885                     tag : 'i',
28886                     cls : 'roo-alert-icon'
28887                     
28888                 },
28889                 {
28890                     tag : 'b',
28891                     cls : 'roo-alert-title',
28892                     html : this.title
28893                 },
28894                 {
28895                     tag : 'span',
28896                     cls : 'roo-alert-text',
28897                     html : this.html
28898                 }
28899             ]
28900         };
28901         
28902         if(this.faicon){
28903             cfg.cn[0].cls += ' fa ' + this.faicon;
28904         }
28905         
28906         if(this.weight){
28907             cfg.cls += ' alert-' + this.weight;
28908         }
28909         
28910         return cfg;
28911     },
28912     
28913     initEvents: function() 
28914     {
28915         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28916     },
28917     
28918     setTitle : function(str)
28919     {
28920         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
28921     },
28922     
28923     setText : function(str)
28924     {
28925         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
28926     },
28927     
28928     setWeight : function(weight)
28929     {
28930         if(this.weight){
28931             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
28932         }
28933         
28934         this.weight = weight;
28935         
28936         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
28937     },
28938     
28939     setIcon : function(icon)
28940     {
28941         if(this.faicon){
28942             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
28943         }
28944         
28945         this.faicon = icon;
28946         
28947         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
28948     },
28949     
28950     hide: function() 
28951     {
28952         this.el.hide();   
28953     },
28954     
28955     show: function() 
28956     {  
28957         this.el.show();   
28958     }
28959     
28960 });
28961
28962  
28963 /*
28964 * Licence: LGPL
28965 */
28966
28967 /**
28968  * @class Roo.bootstrap.UploadCropbox
28969  * @extends Roo.bootstrap.Component
28970  * Bootstrap UploadCropbox class
28971  * @cfg {String} emptyText show when image has been loaded
28972  * @cfg {String} rotateNotify show when image too small to rotate
28973  * @cfg {Number} errorTimeout default 3000
28974  * @cfg {Number} minWidth default 300
28975  * @cfg {Number} minHeight default 300
28976  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
28977  * @cfg {Boolean} isDocument (true|false) default false
28978  * @cfg {String} url action url
28979  * @cfg {String} paramName default 'imageUpload'
28980  * @cfg {String} method default POST
28981  * @cfg {Boolean} loadMask (true|false) default true
28982  * @cfg {Boolean} loadingText default 'Loading...'
28983  * 
28984  * @constructor
28985  * Create a new UploadCropbox
28986  * @param {Object} config The config object
28987  */
28988
28989 Roo.bootstrap.UploadCropbox = function(config){
28990     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
28991     
28992     this.addEvents({
28993         /**
28994          * @event beforeselectfile
28995          * Fire before select file
28996          * @param {Roo.bootstrap.UploadCropbox} this
28997          */
28998         "beforeselectfile" : true,
28999         /**
29000          * @event initial
29001          * Fire after initEvent
29002          * @param {Roo.bootstrap.UploadCropbox} this
29003          */
29004         "initial" : true,
29005         /**
29006          * @event crop
29007          * Fire after initEvent
29008          * @param {Roo.bootstrap.UploadCropbox} this
29009          * @param {String} data
29010          */
29011         "crop" : true,
29012         /**
29013          * @event prepare
29014          * Fire when preparing the file data
29015          * @param {Roo.bootstrap.UploadCropbox} this
29016          * @param {Object} file
29017          */
29018         "prepare" : true,
29019         /**
29020          * @event exception
29021          * Fire when get exception
29022          * @param {Roo.bootstrap.UploadCropbox} this
29023          * @param {XMLHttpRequest} xhr
29024          */
29025         "exception" : true,
29026         /**
29027          * @event beforeloadcanvas
29028          * Fire before load the canvas
29029          * @param {Roo.bootstrap.UploadCropbox} this
29030          * @param {String} src
29031          */
29032         "beforeloadcanvas" : true,
29033         /**
29034          * @event trash
29035          * Fire when trash image
29036          * @param {Roo.bootstrap.UploadCropbox} this
29037          */
29038         "trash" : true,
29039         /**
29040          * @event download
29041          * Fire when download the image
29042          * @param {Roo.bootstrap.UploadCropbox} this
29043          */
29044         "download" : true,
29045         /**
29046          * @event footerbuttonclick
29047          * Fire when footerbuttonclick
29048          * @param {Roo.bootstrap.UploadCropbox} this
29049          * @param {String} type
29050          */
29051         "footerbuttonclick" : true,
29052         /**
29053          * @event resize
29054          * Fire when resize
29055          * @param {Roo.bootstrap.UploadCropbox} this
29056          */
29057         "resize" : true,
29058         /**
29059          * @event rotate
29060          * Fire when rotate the image
29061          * @param {Roo.bootstrap.UploadCropbox} this
29062          * @param {String} pos
29063          */
29064         "rotate" : true,
29065         /**
29066          * @event inspect
29067          * Fire when inspect the file
29068          * @param {Roo.bootstrap.UploadCropbox} this
29069          * @param {Object} file
29070          */
29071         "inspect" : true,
29072         /**
29073          * @event upload
29074          * Fire when xhr upload the file
29075          * @param {Roo.bootstrap.UploadCropbox} this
29076          * @param {Object} data
29077          */
29078         "upload" : true,
29079         /**
29080          * @event arrange
29081          * Fire when arrange the file data
29082          * @param {Roo.bootstrap.UploadCropbox} this
29083          * @param {Object} formData
29084          */
29085         "arrange" : true
29086     });
29087     
29088     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29089 };
29090
29091 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29092     
29093     emptyText : 'Click to upload image',
29094     rotateNotify : 'Image is too small to rotate',
29095     errorTimeout : 3000,
29096     scale : 0,
29097     baseScale : 1,
29098     rotate : 0,
29099     dragable : false,
29100     pinching : false,
29101     mouseX : 0,
29102     mouseY : 0,
29103     cropData : false,
29104     minWidth : 300,
29105     minHeight : 300,
29106     file : false,
29107     exif : {},
29108     baseRotate : 1,
29109     cropType : 'image/jpeg',
29110     buttons : false,
29111     canvasLoaded : false,
29112     isDocument : false,
29113     method : 'POST',
29114     paramName : 'imageUpload',
29115     loadMask : true,
29116     loadingText : 'Loading...',
29117     maskEl : false,
29118     
29119     getAutoCreate : function()
29120     {
29121         var cfg = {
29122             tag : 'div',
29123             cls : 'roo-upload-cropbox',
29124             cn : [
29125                 {
29126                     tag : 'input',
29127                     cls : 'roo-upload-cropbox-selector',
29128                     type : 'file'
29129                 },
29130                 {
29131                     tag : 'div',
29132                     cls : 'roo-upload-cropbox-body',
29133                     style : 'cursor:pointer',
29134                     cn : [
29135                         {
29136                             tag : 'div',
29137                             cls : 'roo-upload-cropbox-preview'
29138                         },
29139                         {
29140                             tag : 'div',
29141                             cls : 'roo-upload-cropbox-thumb'
29142                         },
29143                         {
29144                             tag : 'div',
29145                             cls : 'roo-upload-cropbox-empty-notify',
29146                             html : this.emptyText
29147                         },
29148                         {
29149                             tag : 'div',
29150                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29151                             html : this.rotateNotify
29152                         }
29153                     ]
29154                 },
29155                 {
29156                     tag : 'div',
29157                     cls : 'roo-upload-cropbox-footer',
29158                     cn : {
29159                         tag : 'div',
29160                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29161                         cn : []
29162                     }
29163                 }
29164             ]
29165         };
29166         
29167         return cfg;
29168     },
29169     
29170     onRender : function(ct, position)
29171     {
29172         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29173         
29174         if (this.buttons.length) {
29175             
29176             Roo.each(this.buttons, function(bb) {
29177                 
29178                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29179                 
29180                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29181                 
29182             }, this);
29183         }
29184         
29185         if(this.loadMask){
29186             this.maskEl = this.el;
29187         }
29188     },
29189     
29190     initEvents : function()
29191     {
29192         this.urlAPI = (window.createObjectURL && window) || 
29193                                 (window.URL && URL.revokeObjectURL && URL) || 
29194                                 (window.webkitURL && webkitURL);
29195                         
29196         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29197         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29198         
29199         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29200         this.selectorEl.hide();
29201         
29202         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29203         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29204         
29205         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29206         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29207         this.thumbEl.hide();
29208         
29209         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29210         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29211         
29212         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29213         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29214         this.errorEl.hide();
29215         
29216         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29217         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29218         this.footerEl.hide();
29219         
29220         this.setThumbBoxSize();
29221         
29222         this.bind();
29223         
29224         this.resize();
29225         
29226         this.fireEvent('initial', this);
29227     },
29228
29229     bind : function()
29230     {
29231         var _this = this;
29232         
29233         window.addEventListener("resize", function() { _this.resize(); } );
29234         
29235         this.bodyEl.on('click', this.beforeSelectFile, this);
29236         
29237         if(Roo.isTouch){
29238             this.bodyEl.on('touchstart', this.onTouchStart, this);
29239             this.bodyEl.on('touchmove', this.onTouchMove, this);
29240             this.bodyEl.on('touchend', this.onTouchEnd, this);
29241         }
29242         
29243         if(!Roo.isTouch){
29244             this.bodyEl.on('mousedown', this.onMouseDown, this);
29245             this.bodyEl.on('mousemove', this.onMouseMove, this);
29246             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29247             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29248             Roo.get(document).on('mouseup', this.onMouseUp, this);
29249         }
29250         
29251         this.selectorEl.on('change', this.onFileSelected, this);
29252     },
29253     
29254     reset : function()
29255     {    
29256         this.scale = 0;
29257         this.baseScale = 1;
29258         this.rotate = 0;
29259         this.baseRotate = 1;
29260         this.dragable = false;
29261         this.pinching = false;
29262         this.mouseX = 0;
29263         this.mouseY = 0;
29264         this.cropData = false;
29265         this.notifyEl.dom.innerHTML = this.emptyText;
29266         
29267         this.selectorEl.dom.value = '';
29268         
29269     },
29270     
29271     resize : function()
29272     {
29273         if(this.fireEvent('resize', this) != false){
29274             this.setThumbBoxPosition();
29275             this.setCanvasPosition();
29276         }
29277     },
29278     
29279     onFooterButtonClick : function(e, el, o, type)
29280     {
29281         switch (type) {
29282             case 'rotate-left' :
29283                 this.onRotateLeft(e);
29284                 break;
29285             case 'rotate-right' :
29286                 this.onRotateRight(e);
29287                 break;
29288             case 'picture' :
29289                 this.beforeSelectFile(e);
29290                 break;
29291             case 'trash' :
29292                 this.trash(e);
29293                 break;
29294             case 'crop' :
29295                 this.crop(e);
29296                 break;
29297             case 'download' :
29298                 this.download(e);
29299                 break;
29300             default :
29301                 break;
29302         }
29303         
29304         this.fireEvent('footerbuttonclick', this, type);
29305     },
29306     
29307     beforeSelectFile : function(e)
29308     {
29309         e.preventDefault();
29310         
29311         if(this.fireEvent('beforeselectfile', this) != false){
29312             this.selectorEl.dom.click();
29313         }
29314     },
29315     
29316     onFileSelected : function(e)
29317     {
29318         e.preventDefault();
29319         
29320         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29321             return;
29322         }
29323         
29324         var file = this.selectorEl.dom.files[0];
29325         
29326         if(this.fireEvent('inspect', this, file) != false){
29327             this.prepare(file);
29328         }
29329         
29330     },
29331     
29332     trash : function(e)
29333     {
29334         this.fireEvent('trash', this);
29335     },
29336     
29337     download : function(e)
29338     {
29339         this.fireEvent('download', this);
29340     },
29341     
29342     loadCanvas : function(src)
29343     {   
29344         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29345             
29346             this.reset();
29347             
29348             this.imageEl = document.createElement('img');
29349             
29350             var _this = this;
29351             
29352             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29353             
29354             this.imageEl.src = src;
29355         }
29356     },
29357     
29358     onLoadCanvas : function()
29359     {   
29360         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29361         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29362         
29363         this.bodyEl.un('click', this.beforeSelectFile, this);
29364         
29365         this.notifyEl.hide();
29366         this.thumbEl.show();
29367         this.footerEl.show();
29368         
29369         this.baseRotateLevel();
29370         
29371         if(this.isDocument){
29372             this.setThumbBoxSize();
29373         }
29374         
29375         this.setThumbBoxPosition();
29376         
29377         this.baseScaleLevel();
29378         
29379         this.draw();
29380         
29381         this.resize();
29382         
29383         this.canvasLoaded = true;
29384         
29385         if(this.loadMask){
29386             this.maskEl.unmask();
29387         }
29388         
29389     },
29390     
29391     setCanvasPosition : function()
29392     {   
29393         if(!this.canvasEl){
29394             return;
29395         }
29396         
29397         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29398         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29399         
29400         this.previewEl.setLeft(pw);
29401         this.previewEl.setTop(ph);
29402         
29403     },
29404     
29405     onMouseDown : function(e)
29406     {   
29407         e.stopEvent();
29408         
29409         this.dragable = true;
29410         this.pinching = false;
29411         
29412         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29413             this.dragable = false;
29414             return;
29415         }
29416         
29417         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29418         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29419         
29420     },
29421     
29422     onMouseMove : function(e)
29423     {   
29424         e.stopEvent();
29425         
29426         if(!this.canvasLoaded){
29427             return;
29428         }
29429         
29430         if (!this.dragable){
29431             return;
29432         }
29433         
29434         var minX = Math.ceil(this.thumbEl.getLeft(true));
29435         var minY = Math.ceil(this.thumbEl.getTop(true));
29436         
29437         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29438         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29439         
29440         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29441         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29442         
29443         x = x - this.mouseX;
29444         y = y - this.mouseY;
29445         
29446         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29447         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29448         
29449         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29450         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29451         
29452         this.previewEl.setLeft(bgX);
29453         this.previewEl.setTop(bgY);
29454         
29455         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29456         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29457     },
29458     
29459     onMouseUp : function(e)
29460     {   
29461         e.stopEvent();
29462         
29463         this.dragable = false;
29464     },
29465     
29466     onMouseWheel : function(e)
29467     {   
29468         e.stopEvent();
29469         
29470         this.startScale = this.scale;
29471         
29472         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29473         
29474         if(!this.zoomable()){
29475             this.scale = this.startScale;
29476             return;
29477         }
29478         
29479         this.draw();
29480         
29481         return;
29482     },
29483     
29484     zoomable : function()
29485     {
29486         var minScale = this.thumbEl.getWidth() / this.minWidth;
29487         
29488         if(this.minWidth < this.minHeight){
29489             minScale = this.thumbEl.getHeight() / this.minHeight;
29490         }
29491         
29492         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29493         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29494         
29495         if(
29496                 this.isDocument &&
29497                 (this.rotate == 0 || this.rotate == 180) && 
29498                 (
29499                     width > this.imageEl.OriginWidth || 
29500                     height > this.imageEl.OriginHeight ||
29501                     (width < this.minWidth && height < this.minHeight)
29502                 )
29503         ){
29504             return false;
29505         }
29506         
29507         if(
29508                 this.isDocument &&
29509                 (this.rotate == 90 || this.rotate == 270) && 
29510                 (
29511                     width > this.imageEl.OriginWidth || 
29512                     height > this.imageEl.OriginHeight ||
29513                     (width < this.minHeight && height < this.minWidth)
29514                 )
29515         ){
29516             return false;
29517         }
29518         
29519         if(
29520                 !this.isDocument &&
29521                 (this.rotate == 0 || this.rotate == 180) && 
29522                 (
29523                     width < this.minWidth || 
29524                     width > this.imageEl.OriginWidth || 
29525                     height < this.minHeight || 
29526                     height > this.imageEl.OriginHeight
29527                 )
29528         ){
29529             return false;
29530         }
29531         
29532         if(
29533                 !this.isDocument &&
29534                 (this.rotate == 90 || this.rotate == 270) && 
29535                 (
29536                     width < this.minHeight || 
29537                     width > this.imageEl.OriginWidth || 
29538                     height < this.minWidth || 
29539                     height > this.imageEl.OriginHeight
29540                 )
29541         ){
29542             return false;
29543         }
29544         
29545         return true;
29546         
29547     },
29548     
29549     onRotateLeft : function(e)
29550     {   
29551         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29552             
29553             var minScale = this.thumbEl.getWidth() / this.minWidth;
29554             
29555             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29556             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29557             
29558             this.startScale = this.scale;
29559             
29560             while (this.getScaleLevel() < minScale){
29561             
29562                 this.scale = this.scale + 1;
29563                 
29564                 if(!this.zoomable()){
29565                     break;
29566                 }
29567                 
29568                 if(
29569                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29570                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29571                 ){
29572                     continue;
29573                 }
29574                 
29575                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29576
29577                 this.draw();
29578                 
29579                 return;
29580             }
29581             
29582             this.scale = this.startScale;
29583             
29584             this.onRotateFail();
29585             
29586             return false;
29587         }
29588         
29589         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29590
29591         if(this.isDocument){
29592             this.setThumbBoxSize();
29593             this.setThumbBoxPosition();
29594             this.setCanvasPosition();
29595         }
29596         
29597         this.draw();
29598         
29599         this.fireEvent('rotate', this, 'left');
29600         
29601     },
29602     
29603     onRotateRight : function(e)
29604     {
29605         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29606             
29607             var minScale = this.thumbEl.getWidth() / this.minWidth;
29608         
29609             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29610             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29611             
29612             this.startScale = this.scale;
29613             
29614             while (this.getScaleLevel() < minScale){
29615             
29616                 this.scale = this.scale + 1;
29617                 
29618                 if(!this.zoomable()){
29619                     break;
29620                 }
29621                 
29622                 if(
29623                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29624                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29625                 ){
29626                     continue;
29627                 }
29628                 
29629                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29630
29631                 this.draw();
29632                 
29633                 return;
29634             }
29635             
29636             this.scale = this.startScale;
29637             
29638             this.onRotateFail();
29639             
29640             return false;
29641         }
29642         
29643         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29644
29645         if(this.isDocument){
29646             this.setThumbBoxSize();
29647             this.setThumbBoxPosition();
29648             this.setCanvasPosition();
29649         }
29650         
29651         this.draw();
29652         
29653         this.fireEvent('rotate', this, 'right');
29654     },
29655     
29656     onRotateFail : function()
29657     {
29658         this.errorEl.show(true);
29659         
29660         var _this = this;
29661         
29662         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29663     },
29664     
29665     draw : function()
29666     {
29667         this.previewEl.dom.innerHTML = '';
29668         
29669         var canvasEl = document.createElement("canvas");
29670         
29671         var contextEl = canvasEl.getContext("2d");
29672         
29673         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29674         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29675         var center = this.imageEl.OriginWidth / 2;
29676         
29677         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29678             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29679             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29680             center = this.imageEl.OriginHeight / 2;
29681         }
29682         
29683         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29684         
29685         contextEl.translate(center, center);
29686         contextEl.rotate(this.rotate * Math.PI / 180);
29687
29688         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29689         
29690         this.canvasEl = document.createElement("canvas");
29691         
29692         this.contextEl = this.canvasEl.getContext("2d");
29693         
29694         switch (this.rotate) {
29695             case 0 :
29696                 
29697                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29698                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29699                 
29700                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29701                 
29702                 break;
29703             case 90 : 
29704                 
29705                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29706                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29707                 
29708                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29709                     this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29710                     break;
29711                 }
29712                 
29713                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29714                 
29715                 break;
29716             case 180 :
29717                 
29718                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29719                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29720                 
29721                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29722                     this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29723                     break;
29724                 }
29725                 
29726                 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29727                 
29728                 break;
29729             case 270 :
29730                 
29731                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29732                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29733         
29734                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29735                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29736                     break;
29737                 }
29738                 
29739                 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29740                 
29741                 break;
29742             default : 
29743                 break;
29744         }
29745         
29746         this.previewEl.appendChild(this.canvasEl);
29747         
29748         this.setCanvasPosition();
29749     },
29750     
29751     crop : function()
29752     {
29753         if(!this.canvasLoaded){
29754             return;
29755         }
29756         
29757         var imageCanvas = document.createElement("canvas");
29758         
29759         var imageContext = imageCanvas.getContext("2d");
29760         
29761         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29762         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29763         
29764         var center = imageCanvas.width / 2;
29765         
29766         imageContext.translate(center, center);
29767         
29768         imageContext.rotate(this.rotate * Math.PI / 180);
29769         
29770         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29771         
29772         var canvas = document.createElement("canvas");
29773         
29774         var context = canvas.getContext("2d");
29775                 
29776         canvas.width = this.minWidth;
29777         canvas.height = this.minHeight;
29778
29779         switch (this.rotate) {
29780             case 0 :
29781                 
29782                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29783                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29784                 
29785                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29786                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29787                 
29788                 var targetWidth = this.minWidth - 2 * x;
29789                 var targetHeight = this.minHeight - 2 * y;
29790                 
29791                 var scale = 1;
29792                 
29793                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29794                     scale = targetWidth / width;
29795                 }
29796                 
29797                 if(x > 0 && y == 0){
29798                     scale = targetHeight / height;
29799                 }
29800                 
29801                 if(x > 0 && y > 0){
29802                     scale = targetWidth / width;
29803                     
29804                     if(width < height){
29805                         scale = targetHeight / height;
29806                     }
29807                 }
29808                 
29809                 context.scale(scale, scale);
29810                 
29811                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29812                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29813
29814                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29815                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29816
29817                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29818                 
29819                 break;
29820             case 90 : 
29821                 
29822                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29823                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29824                 
29825                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29826                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29827                 
29828                 var targetWidth = this.minWidth - 2 * x;
29829                 var targetHeight = this.minHeight - 2 * y;
29830                 
29831                 var scale = 1;
29832                 
29833                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29834                     scale = targetWidth / width;
29835                 }
29836                 
29837                 if(x > 0 && y == 0){
29838                     scale = targetHeight / height;
29839                 }
29840                 
29841                 if(x > 0 && y > 0){
29842                     scale = targetWidth / width;
29843                     
29844                     if(width < height){
29845                         scale = targetHeight / height;
29846                     }
29847                 }
29848                 
29849                 context.scale(scale, scale);
29850                 
29851                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29852                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29853
29854                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29855                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29856                 
29857                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29858                 
29859                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29860                 
29861                 break;
29862             case 180 :
29863                 
29864                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29865                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29866                 
29867                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29868                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29869                 
29870                 var targetWidth = this.minWidth - 2 * x;
29871                 var targetHeight = this.minHeight - 2 * y;
29872                 
29873                 var scale = 1;
29874                 
29875                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29876                     scale = targetWidth / width;
29877                 }
29878                 
29879                 if(x > 0 && y == 0){
29880                     scale = targetHeight / height;
29881                 }
29882                 
29883                 if(x > 0 && y > 0){
29884                     scale = targetWidth / width;
29885                     
29886                     if(width < height){
29887                         scale = targetHeight / height;
29888                     }
29889                 }
29890                 
29891                 context.scale(scale, scale);
29892                 
29893                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29894                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29895
29896                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29897                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29898
29899                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29900                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29901                 
29902                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29903                 
29904                 break;
29905             case 270 :
29906                 
29907                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29908                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29909                 
29910                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29911                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29912                 
29913                 var targetWidth = this.minWidth - 2 * x;
29914                 var targetHeight = this.minHeight - 2 * y;
29915                 
29916                 var scale = 1;
29917                 
29918                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29919                     scale = targetWidth / width;
29920                 }
29921                 
29922                 if(x > 0 && y == 0){
29923                     scale = targetHeight / height;
29924                 }
29925                 
29926                 if(x > 0 && y > 0){
29927                     scale = targetWidth / width;
29928                     
29929                     if(width < height){
29930                         scale = targetHeight / height;
29931                     }
29932                 }
29933                 
29934                 context.scale(scale, scale);
29935                 
29936                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29937                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29938
29939                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29940                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29941                 
29942                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29943                 
29944                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29945                 
29946                 break;
29947             default : 
29948                 break;
29949         }
29950         
29951         this.cropData = canvas.toDataURL(this.cropType);
29952         
29953         if(this.fireEvent('crop', this, this.cropData) !== false){
29954             this.process(this.file, this.cropData);
29955         }
29956         
29957         return;
29958         
29959     },
29960     
29961     setThumbBoxSize : function()
29962     {
29963         var width, height;
29964         
29965         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
29966             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
29967             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
29968             
29969             this.minWidth = width;
29970             this.minHeight = height;
29971             
29972             if(this.rotate == 90 || this.rotate == 270){
29973                 this.minWidth = height;
29974                 this.minHeight = width;
29975             }
29976         }
29977         
29978         height = 300;
29979         width = Math.ceil(this.minWidth * height / this.minHeight);
29980         
29981         if(this.minWidth > this.minHeight){
29982             width = 300;
29983             height = Math.ceil(this.minHeight * width / this.minWidth);
29984         }
29985         
29986         this.thumbEl.setStyle({
29987             width : width + 'px',
29988             height : height + 'px'
29989         });
29990
29991         return;
29992             
29993     },
29994     
29995     setThumbBoxPosition : function()
29996     {
29997         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
29998         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
29999         
30000         this.thumbEl.setLeft(x);
30001         this.thumbEl.setTop(y);
30002         
30003     },
30004     
30005     baseRotateLevel : function()
30006     {
30007         this.baseRotate = 1;
30008         
30009         if(
30010                 typeof(this.exif) != 'undefined' &&
30011                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30012                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30013         ){
30014             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30015         }
30016         
30017         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30018         
30019     },
30020     
30021     baseScaleLevel : function()
30022     {
30023         var width, height;
30024         
30025         if(this.isDocument){
30026             
30027             if(this.baseRotate == 6 || this.baseRotate == 8){
30028             
30029                 height = this.thumbEl.getHeight();
30030                 this.baseScale = height / this.imageEl.OriginWidth;
30031
30032                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30033                     width = this.thumbEl.getWidth();
30034                     this.baseScale = width / this.imageEl.OriginHeight;
30035                 }
30036
30037                 return;
30038             }
30039
30040             height = this.thumbEl.getHeight();
30041             this.baseScale = height / this.imageEl.OriginHeight;
30042
30043             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30044                 width = this.thumbEl.getWidth();
30045                 this.baseScale = width / this.imageEl.OriginWidth;
30046             }
30047
30048             return;
30049         }
30050         
30051         if(this.baseRotate == 6 || this.baseRotate == 8){
30052             
30053             width = this.thumbEl.getHeight();
30054             this.baseScale = width / this.imageEl.OriginHeight;
30055             
30056             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30057                 height = this.thumbEl.getWidth();
30058                 this.baseScale = height / this.imageEl.OriginHeight;
30059             }
30060             
30061             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30062                 height = this.thumbEl.getWidth();
30063                 this.baseScale = height / this.imageEl.OriginHeight;
30064                 
30065                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30066                     width = this.thumbEl.getHeight();
30067                     this.baseScale = width / this.imageEl.OriginWidth;
30068                 }
30069             }
30070             
30071             return;
30072         }
30073         
30074         width = this.thumbEl.getWidth();
30075         this.baseScale = width / this.imageEl.OriginWidth;
30076         
30077         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30078             height = this.thumbEl.getHeight();
30079             this.baseScale = height / this.imageEl.OriginHeight;
30080         }
30081         
30082         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30083             
30084             height = this.thumbEl.getHeight();
30085             this.baseScale = height / this.imageEl.OriginHeight;
30086             
30087             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30088                 width = this.thumbEl.getWidth();
30089                 this.baseScale = width / this.imageEl.OriginWidth;
30090             }
30091             
30092         }
30093         
30094         return;
30095     },
30096     
30097     getScaleLevel : function()
30098     {
30099         return this.baseScale * Math.pow(1.1, this.scale);
30100     },
30101     
30102     onTouchStart : function(e)
30103     {
30104         if(!this.canvasLoaded){
30105             this.beforeSelectFile(e);
30106             return;
30107         }
30108         
30109         var touches = e.browserEvent.touches;
30110         
30111         if(!touches){
30112             return;
30113         }
30114         
30115         if(touches.length == 1){
30116             this.onMouseDown(e);
30117             return;
30118         }
30119         
30120         if(touches.length != 2){
30121             return;
30122         }
30123         
30124         var coords = [];
30125         
30126         for(var i = 0, finger; finger = touches[i]; i++){
30127             coords.push(finger.pageX, finger.pageY);
30128         }
30129         
30130         var x = Math.pow(coords[0] - coords[2], 2);
30131         var y = Math.pow(coords[1] - coords[3], 2);
30132         
30133         this.startDistance = Math.sqrt(x + y);
30134         
30135         this.startScale = this.scale;
30136         
30137         this.pinching = true;
30138         this.dragable = false;
30139         
30140     },
30141     
30142     onTouchMove : function(e)
30143     {
30144         if(!this.pinching && !this.dragable){
30145             return;
30146         }
30147         
30148         var touches = e.browserEvent.touches;
30149         
30150         if(!touches){
30151             return;
30152         }
30153         
30154         if(this.dragable){
30155             this.onMouseMove(e);
30156             return;
30157         }
30158         
30159         var coords = [];
30160         
30161         for(var i = 0, finger; finger = touches[i]; i++){
30162             coords.push(finger.pageX, finger.pageY);
30163         }
30164         
30165         var x = Math.pow(coords[0] - coords[2], 2);
30166         var y = Math.pow(coords[1] - coords[3], 2);
30167         
30168         this.endDistance = Math.sqrt(x + y);
30169         
30170         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30171         
30172         if(!this.zoomable()){
30173             this.scale = this.startScale;
30174             return;
30175         }
30176         
30177         this.draw();
30178         
30179     },
30180     
30181     onTouchEnd : function(e)
30182     {
30183         this.pinching = false;
30184         this.dragable = false;
30185         
30186     },
30187     
30188     process : function(file, crop)
30189     {
30190         if(this.loadMask){
30191             this.maskEl.mask(this.loadingText);
30192         }
30193         
30194         this.xhr = new XMLHttpRequest();
30195         
30196         file.xhr = this.xhr;
30197
30198         this.xhr.open(this.method, this.url, true);
30199         
30200         var headers = {
30201             "Accept": "application/json",
30202             "Cache-Control": "no-cache",
30203             "X-Requested-With": "XMLHttpRequest"
30204         };
30205         
30206         for (var headerName in headers) {
30207             var headerValue = headers[headerName];
30208             if (headerValue) {
30209                 this.xhr.setRequestHeader(headerName, headerValue);
30210             }
30211         }
30212         
30213         var _this = this;
30214         
30215         this.xhr.onload = function()
30216         {
30217             _this.xhrOnLoad(_this.xhr);
30218         }
30219         
30220         this.xhr.onerror = function()
30221         {
30222             _this.xhrOnError(_this.xhr);
30223         }
30224         
30225         var formData = new FormData();
30226
30227         formData.append('returnHTML', 'NO');
30228         
30229         if(crop){
30230             formData.append('crop', crop);
30231         }
30232         
30233         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30234             formData.append(this.paramName, file, file.name);
30235         }
30236         
30237         if(typeof(file.filename) != 'undefined'){
30238             formData.append('filename', file.filename);
30239         }
30240         
30241         if(typeof(file.mimetype) != 'undefined'){
30242             formData.append('mimetype', file.mimetype);
30243         }
30244         
30245         if(this.fireEvent('arrange', this, formData) != false){
30246             this.xhr.send(formData);
30247         };
30248     },
30249     
30250     xhrOnLoad : function(xhr)
30251     {
30252         if(this.loadMask){
30253             this.maskEl.unmask();
30254         }
30255         
30256         if (xhr.readyState !== 4) {
30257             this.fireEvent('exception', this, xhr);
30258             return;
30259         }
30260
30261         var response = Roo.decode(xhr.responseText);
30262         
30263         if(!response.success){
30264             this.fireEvent('exception', this, xhr);
30265             return;
30266         }
30267         
30268         var response = Roo.decode(xhr.responseText);
30269         
30270         this.fireEvent('upload', this, response);
30271         
30272     },
30273     
30274     xhrOnError : function()
30275     {
30276         if(this.loadMask){
30277             this.maskEl.unmask();
30278         }
30279         
30280         Roo.log('xhr on error');
30281         
30282         var response = Roo.decode(xhr.responseText);
30283           
30284         Roo.log(response);
30285         
30286     },
30287     
30288     prepare : function(file)
30289     {   
30290         if(this.loadMask){
30291             this.maskEl.mask(this.loadingText);
30292         }
30293         
30294         this.file = false;
30295         this.exif = {};
30296         
30297         if(typeof(file) === 'string'){
30298             this.loadCanvas(file);
30299             return;
30300         }
30301         
30302         if(!file || !this.urlAPI){
30303             return;
30304         }
30305         
30306         this.file = file;
30307         this.cropType = file.type;
30308         
30309         var _this = this;
30310         
30311         if(this.fireEvent('prepare', this, this.file) != false){
30312             
30313             var reader = new FileReader();
30314             
30315             reader.onload = function (e) {
30316                 if (e.target.error) {
30317                     Roo.log(e.target.error);
30318                     return;
30319                 }
30320                 
30321                 var buffer = e.target.result,
30322                     dataView = new DataView(buffer),
30323                     offset = 2,
30324                     maxOffset = dataView.byteLength - 4,
30325                     markerBytes,
30326                     markerLength;
30327                 
30328                 if (dataView.getUint16(0) === 0xffd8) {
30329                     while (offset < maxOffset) {
30330                         markerBytes = dataView.getUint16(offset);
30331                         
30332                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30333                             markerLength = dataView.getUint16(offset + 2) + 2;
30334                             if (offset + markerLength > dataView.byteLength) {
30335                                 Roo.log('Invalid meta data: Invalid segment size.');
30336                                 break;
30337                             }
30338                             
30339                             if(markerBytes == 0xffe1){
30340                                 _this.parseExifData(
30341                                     dataView,
30342                                     offset,
30343                                     markerLength
30344                                 );
30345                             }
30346                             
30347                             offset += markerLength;
30348                             
30349                             continue;
30350                         }
30351                         
30352                         break;
30353                     }
30354                     
30355                 }
30356                 
30357                 var url = _this.urlAPI.createObjectURL(_this.file);
30358                 
30359                 _this.loadCanvas(url);
30360                 
30361                 return;
30362             }
30363             
30364             reader.readAsArrayBuffer(this.file);
30365             
30366         }
30367         
30368     },
30369     
30370     parseExifData : function(dataView, offset, length)
30371     {
30372         var tiffOffset = offset + 10,
30373             littleEndian,
30374             dirOffset;
30375     
30376         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30377             // No Exif data, might be XMP data instead
30378             return;
30379         }
30380         
30381         // Check for the ASCII code for "Exif" (0x45786966):
30382         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30383             // No Exif data, might be XMP data instead
30384             return;
30385         }
30386         if (tiffOffset + 8 > dataView.byteLength) {
30387             Roo.log('Invalid Exif data: Invalid segment size.');
30388             return;
30389         }
30390         // Check for the two null bytes:
30391         if (dataView.getUint16(offset + 8) !== 0x0000) {
30392             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30393             return;
30394         }
30395         // Check the byte alignment:
30396         switch (dataView.getUint16(tiffOffset)) {
30397         case 0x4949:
30398             littleEndian = true;
30399             break;
30400         case 0x4D4D:
30401             littleEndian = false;
30402             break;
30403         default:
30404             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30405             return;
30406         }
30407         // Check for the TIFF tag marker (0x002A):
30408         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30409             Roo.log('Invalid Exif data: Missing TIFF marker.');
30410             return;
30411         }
30412         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30413         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30414         
30415         this.parseExifTags(
30416             dataView,
30417             tiffOffset,
30418             tiffOffset + dirOffset,
30419             littleEndian
30420         );
30421     },
30422     
30423     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30424     {
30425         var tagsNumber,
30426             dirEndOffset,
30427             i;
30428         if (dirOffset + 6 > dataView.byteLength) {
30429             Roo.log('Invalid Exif data: Invalid directory offset.');
30430             return;
30431         }
30432         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30433         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30434         if (dirEndOffset + 4 > dataView.byteLength) {
30435             Roo.log('Invalid Exif data: Invalid directory size.');
30436             return;
30437         }
30438         for (i = 0; i < tagsNumber; i += 1) {
30439             this.parseExifTag(
30440                 dataView,
30441                 tiffOffset,
30442                 dirOffset + 2 + 12 * i, // tag offset
30443                 littleEndian
30444             );
30445         }
30446         // Return the offset to the next directory:
30447         return dataView.getUint32(dirEndOffset, littleEndian);
30448     },
30449     
30450     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30451     {
30452         var tag = dataView.getUint16(offset, littleEndian);
30453         
30454         this.exif[tag] = this.getExifValue(
30455             dataView,
30456             tiffOffset,
30457             offset,
30458             dataView.getUint16(offset + 2, littleEndian), // tag type
30459             dataView.getUint32(offset + 4, littleEndian), // tag length
30460             littleEndian
30461         );
30462     },
30463     
30464     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30465     {
30466         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30467             tagSize,
30468             dataOffset,
30469             values,
30470             i,
30471             str,
30472             c;
30473     
30474         if (!tagType) {
30475             Roo.log('Invalid Exif data: Invalid tag type.');
30476             return;
30477         }
30478         
30479         tagSize = tagType.size * length;
30480         // Determine if the value is contained in the dataOffset bytes,
30481         // or if the value at the dataOffset is a pointer to the actual data:
30482         dataOffset = tagSize > 4 ?
30483                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30484         if (dataOffset + tagSize > dataView.byteLength) {
30485             Roo.log('Invalid Exif data: Invalid data offset.');
30486             return;
30487         }
30488         if (length === 1) {
30489             return tagType.getValue(dataView, dataOffset, littleEndian);
30490         }
30491         values = [];
30492         for (i = 0; i < length; i += 1) {
30493             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30494         }
30495         
30496         if (tagType.ascii) {
30497             str = '';
30498             // Concatenate the chars:
30499             for (i = 0; i < values.length; i += 1) {
30500                 c = values[i];
30501                 // Ignore the terminating NULL byte(s):
30502                 if (c === '\u0000') {
30503                     break;
30504                 }
30505                 str += c;
30506             }
30507             return str;
30508         }
30509         return values;
30510     }
30511     
30512 });
30513
30514 Roo.apply(Roo.bootstrap.UploadCropbox, {
30515     tags : {
30516         'Orientation': 0x0112
30517     },
30518     
30519     Orientation: {
30520             1: 0, //'top-left',
30521 //            2: 'top-right',
30522             3: 180, //'bottom-right',
30523 //            4: 'bottom-left',
30524 //            5: 'left-top',
30525             6: 90, //'right-top',
30526 //            7: 'right-bottom',
30527             8: 270 //'left-bottom'
30528     },
30529     
30530     exifTagTypes : {
30531         // byte, 8-bit unsigned int:
30532         1: {
30533             getValue: function (dataView, dataOffset) {
30534                 return dataView.getUint8(dataOffset);
30535             },
30536             size: 1
30537         },
30538         // ascii, 8-bit byte:
30539         2: {
30540             getValue: function (dataView, dataOffset) {
30541                 return String.fromCharCode(dataView.getUint8(dataOffset));
30542             },
30543             size: 1,
30544             ascii: true
30545         },
30546         // short, 16 bit int:
30547         3: {
30548             getValue: function (dataView, dataOffset, littleEndian) {
30549                 return dataView.getUint16(dataOffset, littleEndian);
30550             },
30551             size: 2
30552         },
30553         // long, 32 bit int:
30554         4: {
30555             getValue: function (dataView, dataOffset, littleEndian) {
30556                 return dataView.getUint32(dataOffset, littleEndian);
30557             },
30558             size: 4
30559         },
30560         // rational = two long values, first is numerator, second is denominator:
30561         5: {
30562             getValue: function (dataView, dataOffset, littleEndian) {
30563                 return dataView.getUint32(dataOffset, littleEndian) /
30564                     dataView.getUint32(dataOffset + 4, littleEndian);
30565             },
30566             size: 8
30567         },
30568         // slong, 32 bit signed int:
30569         9: {
30570             getValue: function (dataView, dataOffset, littleEndian) {
30571                 return dataView.getInt32(dataOffset, littleEndian);
30572             },
30573             size: 4
30574         },
30575         // srational, two slongs, first is numerator, second is denominator:
30576         10: {
30577             getValue: function (dataView, dataOffset, littleEndian) {
30578                 return dataView.getInt32(dataOffset, littleEndian) /
30579                     dataView.getInt32(dataOffset + 4, littleEndian);
30580             },
30581             size: 8
30582         }
30583     },
30584     
30585     footer : {
30586         STANDARD : [
30587             {
30588                 tag : 'div',
30589                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30590                 action : 'rotate-left',
30591                 cn : [
30592                     {
30593                         tag : 'button',
30594                         cls : 'btn btn-default',
30595                         html : '<i class="fa fa-undo"></i>'
30596                     }
30597                 ]
30598             },
30599             {
30600                 tag : 'div',
30601                 cls : 'btn-group roo-upload-cropbox-picture',
30602                 action : 'picture',
30603                 cn : [
30604                     {
30605                         tag : 'button',
30606                         cls : 'btn btn-default',
30607                         html : '<i class="fa fa-picture-o"></i>'
30608                     }
30609                 ]
30610             },
30611             {
30612                 tag : 'div',
30613                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30614                 action : 'rotate-right',
30615                 cn : [
30616                     {
30617                         tag : 'button',
30618                         cls : 'btn btn-default',
30619                         html : '<i class="fa fa-repeat"></i>'
30620                     }
30621                 ]
30622             }
30623         ],
30624         DOCUMENT : [
30625             {
30626                 tag : 'div',
30627                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30628                 action : 'rotate-left',
30629                 cn : [
30630                     {
30631                         tag : 'button',
30632                         cls : 'btn btn-default',
30633                         html : '<i class="fa fa-undo"></i>'
30634                     }
30635                 ]
30636             },
30637             {
30638                 tag : 'div',
30639                 cls : 'btn-group roo-upload-cropbox-download',
30640                 action : 'download',
30641                 cn : [
30642                     {
30643                         tag : 'button',
30644                         cls : 'btn btn-default',
30645                         html : '<i class="fa fa-download"></i>'
30646                     }
30647                 ]
30648             },
30649             {
30650                 tag : 'div',
30651                 cls : 'btn-group roo-upload-cropbox-crop',
30652                 action : 'crop',
30653                 cn : [
30654                     {
30655                         tag : 'button',
30656                         cls : 'btn btn-default',
30657                         html : '<i class="fa fa-crop"></i>'
30658                     }
30659                 ]
30660             },
30661             {
30662                 tag : 'div',
30663                 cls : 'btn-group roo-upload-cropbox-trash',
30664                 action : 'trash',
30665                 cn : [
30666                     {
30667                         tag : 'button',
30668                         cls : 'btn btn-default',
30669                         html : '<i class="fa fa-trash"></i>'
30670                     }
30671                 ]
30672             },
30673             {
30674                 tag : 'div',
30675                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30676                 action : 'rotate-right',
30677                 cn : [
30678                     {
30679                         tag : 'button',
30680                         cls : 'btn btn-default',
30681                         html : '<i class="fa fa-repeat"></i>'
30682                     }
30683                 ]
30684             }
30685         ],
30686         ROTATOR : [
30687             {
30688                 tag : 'div',
30689                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30690                 action : 'rotate-left',
30691                 cn : [
30692                     {
30693                         tag : 'button',
30694                         cls : 'btn btn-default',
30695                         html : '<i class="fa fa-undo"></i>'
30696                     }
30697                 ]
30698             },
30699             {
30700                 tag : 'div',
30701                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30702                 action : 'rotate-right',
30703                 cn : [
30704                     {
30705                         tag : 'button',
30706                         cls : 'btn btn-default',
30707                         html : '<i class="fa fa-repeat"></i>'
30708                     }
30709                 ]
30710             }
30711         ]
30712     }
30713 });
30714
30715 /*
30716 * Licence: LGPL
30717 */
30718
30719 /**
30720  * @class Roo.bootstrap.DocumentManager
30721  * @extends Roo.bootstrap.Component
30722  * Bootstrap DocumentManager class
30723  * @cfg {String} paramName default 'imageUpload'
30724  * @cfg {String} toolTipName default 'filename'
30725  * @cfg {String} method default POST
30726  * @cfg {String} url action url
30727  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30728  * @cfg {Boolean} multiple multiple upload default true
30729  * @cfg {Number} thumbSize default 300
30730  * @cfg {String} fieldLabel
30731  * @cfg {Number} labelWidth default 4
30732  * @cfg {String} labelAlign (left|top) default left
30733  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30734 * @cfg {Number} labellg set the width of label (1-12)
30735  * @cfg {Number} labelmd set the width of label (1-12)
30736  * @cfg {Number} labelsm set the width of label (1-12)
30737  * @cfg {Number} labelxs set the width of label (1-12)
30738  * 
30739  * @constructor
30740  * Create a new DocumentManager
30741  * @param {Object} config The config object
30742  */
30743
30744 Roo.bootstrap.DocumentManager = function(config){
30745     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30746     
30747     this.files = [];
30748     this.delegates = [];
30749     
30750     this.addEvents({
30751         /**
30752          * @event initial
30753          * Fire when initial the DocumentManager
30754          * @param {Roo.bootstrap.DocumentManager} this
30755          */
30756         "initial" : true,
30757         /**
30758          * @event inspect
30759          * inspect selected file
30760          * @param {Roo.bootstrap.DocumentManager} this
30761          * @param {File} file
30762          */
30763         "inspect" : true,
30764         /**
30765          * @event exception
30766          * Fire when xhr load exception
30767          * @param {Roo.bootstrap.DocumentManager} this
30768          * @param {XMLHttpRequest} xhr
30769          */
30770         "exception" : true,
30771         /**
30772          * @event afterupload
30773          * Fire when xhr load exception
30774          * @param {Roo.bootstrap.DocumentManager} this
30775          * @param {XMLHttpRequest} xhr
30776          */
30777         "afterupload" : true,
30778         /**
30779          * @event prepare
30780          * prepare the form data
30781          * @param {Roo.bootstrap.DocumentManager} this
30782          * @param {Object} formData
30783          */
30784         "prepare" : true,
30785         /**
30786          * @event remove
30787          * Fire when remove the file
30788          * @param {Roo.bootstrap.DocumentManager} this
30789          * @param {Object} file
30790          */
30791         "remove" : true,
30792         /**
30793          * @event refresh
30794          * Fire after refresh the file
30795          * @param {Roo.bootstrap.DocumentManager} this
30796          */
30797         "refresh" : true,
30798         /**
30799          * @event click
30800          * Fire after click the image
30801          * @param {Roo.bootstrap.DocumentManager} this
30802          * @param {Object} file
30803          */
30804         "click" : true,
30805         /**
30806          * @event edit
30807          * Fire when upload a image and editable set to true
30808          * @param {Roo.bootstrap.DocumentManager} this
30809          * @param {Object} file
30810          */
30811         "edit" : true,
30812         /**
30813          * @event beforeselectfile
30814          * Fire before select file
30815          * @param {Roo.bootstrap.DocumentManager} this
30816          */
30817         "beforeselectfile" : true,
30818         /**
30819          * @event process
30820          * Fire before process file
30821          * @param {Roo.bootstrap.DocumentManager} this
30822          * @param {Object} file
30823          */
30824         "process" : true,
30825         /**
30826          * @event previewrendered
30827          * Fire when preview rendered
30828          * @param {Roo.bootstrap.DocumentManager} this
30829          * @param {Object} file
30830          */
30831         "previewrendered" : true,
30832         /**
30833          */
30834         "previewResize" : true
30835         
30836     });
30837 };
30838
30839 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
30840     
30841     boxes : 0,
30842     inputName : '',
30843     thumbSize : 300,
30844     multiple : true,
30845     files : false,
30846     method : 'POST',
30847     url : '',
30848     paramName : 'imageUpload',
30849     toolTipName : 'filename',
30850     fieldLabel : '',
30851     labelWidth : 4,
30852     labelAlign : 'left',
30853     editable : true,
30854     delegates : false,
30855     xhr : false, 
30856     
30857     labellg : 0,
30858     labelmd : 0,
30859     labelsm : 0,
30860     labelxs : 0,
30861     
30862     getAutoCreate : function()
30863     {   
30864         var managerWidget = {
30865             tag : 'div',
30866             cls : 'roo-document-manager',
30867             cn : [
30868                 {
30869                     tag : 'input',
30870                     cls : 'roo-document-manager-selector',
30871                     type : 'file'
30872                 },
30873                 {
30874                     tag : 'div',
30875                     cls : 'roo-document-manager-uploader',
30876                     cn : [
30877                         {
30878                             tag : 'div',
30879                             cls : 'roo-document-manager-upload-btn',
30880                             html : '<i class="fa fa-plus"></i>'
30881                         }
30882                     ]
30883                     
30884                 }
30885             ]
30886         };
30887         
30888         var content = [
30889             {
30890                 tag : 'div',
30891                 cls : 'column col-md-12',
30892                 cn : managerWidget
30893             }
30894         ];
30895         
30896         if(this.fieldLabel.length){
30897             
30898             content = [
30899                 {
30900                     tag : 'div',
30901                     cls : 'column col-md-12',
30902                     html : this.fieldLabel
30903                 },
30904                 {
30905                     tag : 'div',
30906                     cls : 'column col-md-12',
30907                     cn : managerWidget
30908                 }
30909             ];
30910
30911             if(this.labelAlign == 'left'){
30912                 content = [
30913                     {
30914                         tag : 'div',
30915                         cls : 'column',
30916                         html : this.fieldLabel
30917                     },
30918                     {
30919                         tag : 'div',
30920                         cls : 'column',
30921                         cn : managerWidget
30922                     }
30923                 ];
30924                 
30925                 if(this.labelWidth > 12){
30926                     content[0].style = "width: " + this.labelWidth + 'px';
30927                 }
30928
30929                 if(this.labelWidth < 13 && this.labelmd == 0){
30930                     this.labelmd = this.labelWidth;
30931                 }
30932
30933                 if(this.labellg > 0){
30934                     content[0].cls += ' col-lg-' + this.labellg;
30935                     content[1].cls += ' col-lg-' + (12 - this.labellg);
30936                 }
30937
30938                 if(this.labelmd > 0){
30939                     content[0].cls += ' col-md-' + this.labelmd;
30940                     content[1].cls += ' col-md-' + (12 - this.labelmd);
30941                 }
30942
30943                 if(this.labelsm > 0){
30944                     content[0].cls += ' col-sm-' + this.labelsm;
30945                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
30946                 }
30947
30948                 if(this.labelxs > 0){
30949                     content[0].cls += ' col-xs-' + this.labelxs;
30950                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
30951                 }
30952                 
30953             }
30954         }
30955         
30956         var cfg = {
30957             tag : 'div',
30958             cls : 'row clearfix',
30959             cn : content
30960         };
30961         
30962         return cfg;
30963         
30964     },
30965     
30966     initEvents : function()
30967     {
30968         this.managerEl = this.el.select('.roo-document-manager', true).first();
30969         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30970         
30971         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
30972         this.selectorEl.hide();
30973         
30974         if(this.multiple){
30975             this.selectorEl.attr('multiple', 'multiple');
30976         }
30977         
30978         this.selectorEl.on('change', this.onFileSelected, this);
30979         
30980         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
30981         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30982         
30983         this.uploader.on('click', this.onUploaderClick, this);
30984         
30985         this.renderProgressDialog();
30986         
30987         var _this = this;
30988         
30989         window.addEventListener("resize", function() { _this.refresh(); } );
30990         
30991         this.fireEvent('initial', this);
30992     },
30993     
30994     renderProgressDialog : function()
30995     {
30996         var _this = this;
30997         
30998         this.progressDialog = new Roo.bootstrap.Modal({
30999             cls : 'roo-document-manager-progress-dialog',
31000             allow_close : false,
31001             animate : false,
31002             title : '',
31003             buttons : [
31004                 {
31005                     name  :'cancel',
31006                     weight : 'danger',
31007                     html : 'Cancel'
31008                 }
31009             ], 
31010             listeners : { 
31011                 btnclick : function() {
31012                     _this.uploadCancel();
31013                     this.hide();
31014                 }
31015             }
31016         });
31017          
31018         this.progressDialog.render(Roo.get(document.body));
31019          
31020         this.progress = new Roo.bootstrap.Progress({
31021             cls : 'roo-document-manager-progress',
31022             active : true,
31023             striped : true
31024         });
31025         
31026         this.progress.render(this.progressDialog.getChildContainer());
31027         
31028         this.progressBar = new Roo.bootstrap.ProgressBar({
31029             cls : 'roo-document-manager-progress-bar',
31030             aria_valuenow : 0,
31031             aria_valuemin : 0,
31032             aria_valuemax : 12,
31033             panel : 'success'
31034         });
31035         
31036         this.progressBar.render(this.progress.getChildContainer());
31037     },
31038     
31039     onUploaderClick : function(e)
31040     {
31041         e.preventDefault();
31042      
31043         if(this.fireEvent('beforeselectfile', this) != false){
31044             this.selectorEl.dom.click();
31045         }
31046         
31047     },
31048     
31049     onFileSelected : function(e)
31050     {
31051         e.preventDefault();
31052         
31053         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31054             return;
31055         }
31056         
31057         Roo.each(this.selectorEl.dom.files, function(file){
31058             if(this.fireEvent('inspect', this, file) != false){
31059                 this.files.push(file);
31060             }
31061         }, this);
31062         
31063         this.queue();
31064         
31065     },
31066     
31067     queue : function()
31068     {
31069         this.selectorEl.dom.value = '';
31070         
31071         if(!this.files || !this.files.length){
31072             return;
31073         }
31074         
31075         if(this.boxes > 0 && this.files.length > this.boxes){
31076             this.files = this.files.slice(0, this.boxes);
31077         }
31078         
31079         this.uploader.show();
31080         
31081         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31082             this.uploader.hide();
31083         }
31084         
31085         var _this = this;
31086         
31087         var files = [];
31088         
31089         var docs = [];
31090         
31091         Roo.each(this.files, function(file){
31092             
31093             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31094                 var f = this.renderPreview(file);
31095                 files.push(f);
31096                 return;
31097             }
31098             
31099             if(file.type.indexOf('image') != -1){
31100                 this.delegates.push(
31101                     (function(){
31102                         _this.process(file);
31103                     }).createDelegate(this)
31104                 );
31105         
31106                 return;
31107             }
31108             
31109             docs.push(
31110                 (function(){
31111                     _this.process(file);
31112                 }).createDelegate(this)
31113             );
31114             
31115         }, this);
31116         
31117         this.files = files;
31118         
31119         this.delegates = this.delegates.concat(docs);
31120         
31121         if(!this.delegates.length){
31122             this.refresh();
31123             return;
31124         }
31125         
31126         this.progressBar.aria_valuemax = this.delegates.length;
31127         
31128         this.arrange();
31129         
31130         return;
31131     },
31132     
31133     arrange : function()
31134     {
31135         if(!this.delegates.length){
31136             this.progressDialog.hide();
31137             this.refresh();
31138             return;
31139         }
31140         
31141         var delegate = this.delegates.shift();
31142         
31143         this.progressDialog.show();
31144         
31145         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31146         
31147         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31148         
31149         delegate();
31150     },
31151     
31152     refresh : function()
31153     {
31154         this.uploader.show();
31155         
31156         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31157             this.uploader.hide();
31158         }
31159         
31160         Roo.isTouch ? this.closable(false) : this.closable(true);
31161         
31162         this.fireEvent('refresh', this);
31163     },
31164     
31165     onRemove : function(e, el, o)
31166     {
31167         e.preventDefault();
31168         
31169         this.fireEvent('remove', this, o);
31170         
31171     },
31172     
31173     remove : function(o)
31174     {
31175         var files = [];
31176         
31177         Roo.each(this.files, function(file){
31178             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31179                 files.push(file);
31180                 return;
31181             }
31182
31183             o.target.remove();
31184
31185         }, this);
31186         
31187         this.files = files;
31188         
31189         this.refresh();
31190     },
31191     
31192     clear : function()
31193     {
31194         Roo.each(this.files, function(file){
31195             if(!file.target){
31196                 return;
31197             }
31198             
31199             file.target.remove();
31200
31201         }, this);
31202         
31203         this.files = [];
31204         
31205         this.refresh();
31206     },
31207     
31208     onClick : function(e, el, o)
31209     {
31210         e.preventDefault();
31211         
31212         this.fireEvent('click', this, o);
31213         
31214     },
31215     
31216     closable : function(closable)
31217     {
31218         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31219             
31220             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31221             
31222             if(closable){
31223                 el.show();
31224                 return;
31225             }
31226             
31227             el.hide();
31228             
31229         }, this);
31230     },
31231     
31232     xhrOnLoad : function(xhr)
31233     {
31234         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31235             el.remove();
31236         }, this);
31237         
31238         if (xhr.readyState !== 4) {
31239             this.arrange();
31240             this.fireEvent('exception', this, xhr);
31241             return;
31242         }
31243
31244         var response = Roo.decode(xhr.responseText);
31245         
31246         if(!response.success){
31247             this.arrange();
31248             this.fireEvent('exception', this, xhr);
31249             return;
31250         }
31251         
31252         var file = this.renderPreview(response.data);
31253         
31254         this.files.push(file);
31255         
31256         this.arrange();
31257         
31258         this.fireEvent('afterupload', this, xhr);
31259         
31260     },
31261     
31262     xhrOnError : function(xhr)
31263     {
31264         Roo.log('xhr on error');
31265         
31266         var response = Roo.decode(xhr.responseText);
31267           
31268         Roo.log(response);
31269         
31270         this.arrange();
31271     },
31272     
31273     process : function(file)
31274     {
31275         if(this.fireEvent('process', this, file) !== false){
31276             if(this.editable && file.type.indexOf('image') != -1){
31277                 this.fireEvent('edit', this, file);
31278                 return;
31279             }
31280
31281             this.uploadStart(file, false);
31282
31283             return;
31284         }
31285         
31286     },
31287     
31288     uploadStart : function(file, crop)
31289     {
31290         this.xhr = new XMLHttpRequest();
31291         
31292         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31293             this.arrange();
31294             return;
31295         }
31296         
31297         file.xhr = this.xhr;
31298             
31299         this.managerEl.createChild({
31300             tag : 'div',
31301             cls : 'roo-document-manager-loading',
31302             cn : [
31303                 {
31304                     tag : 'div',
31305                     tooltip : file.name,
31306                     cls : 'roo-document-manager-thumb',
31307                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31308                 }
31309             ]
31310
31311         });
31312
31313         this.xhr.open(this.method, this.url, true);
31314         
31315         var headers = {
31316             "Accept": "application/json",
31317             "Cache-Control": "no-cache",
31318             "X-Requested-With": "XMLHttpRequest"
31319         };
31320         
31321         for (var headerName in headers) {
31322             var headerValue = headers[headerName];
31323             if (headerValue) {
31324                 this.xhr.setRequestHeader(headerName, headerValue);
31325             }
31326         }
31327         
31328         var _this = this;
31329         
31330         this.xhr.onload = function()
31331         {
31332             _this.xhrOnLoad(_this.xhr);
31333         }
31334         
31335         this.xhr.onerror = function()
31336         {
31337             _this.xhrOnError(_this.xhr);
31338         }
31339         
31340         var formData = new FormData();
31341
31342         formData.append('returnHTML', 'NO');
31343         
31344         if(crop){
31345             formData.append('crop', crop);
31346         }
31347         
31348         formData.append(this.paramName, file, file.name);
31349         
31350         var options = {
31351             file : file, 
31352             manually : false
31353         };
31354         
31355         if(this.fireEvent('prepare', this, formData, options) != false){
31356             
31357             if(options.manually){
31358                 return;
31359             }
31360             
31361             this.xhr.send(formData);
31362             return;
31363         };
31364         
31365         this.uploadCancel();
31366     },
31367     
31368     uploadCancel : function()
31369     {
31370         if (this.xhr) {
31371             this.xhr.abort();
31372         }
31373         
31374         this.delegates = [];
31375         
31376         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31377             el.remove();
31378         }, this);
31379         
31380         this.arrange();
31381     },
31382     
31383     renderPreview : function(file)
31384     {
31385         if(typeof(file.target) != 'undefined' && file.target){
31386             return file;
31387         }
31388         
31389         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31390         
31391         var previewEl = this.managerEl.createChild({
31392             tag : 'div',
31393             cls : 'roo-document-manager-preview',
31394             cn : [
31395                 {
31396                     tag : 'div',
31397                     tooltip : file[this.toolTipName],
31398                     cls : 'roo-document-manager-thumb',
31399                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31400                 },
31401                 {
31402                     tag : 'button',
31403                     cls : 'close',
31404                     html : '<i class="fa fa-times-circle"></i>'
31405                 }
31406             ]
31407         });
31408
31409         var close = previewEl.select('button.close', true).first();
31410
31411         close.on('click', this.onRemove, this, file);
31412
31413         file.target = previewEl;
31414
31415         var image = previewEl.select('img', true).first();
31416         
31417         var _this = this;
31418         
31419         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31420         
31421         image.on('click', this.onClick, this, file);
31422         
31423         this.fireEvent('previewrendered', this, file);
31424         
31425         return file;
31426         
31427     },
31428     
31429     onPreviewLoad : function(file, image)
31430     {
31431         if(typeof(file.target) == 'undefined' || !file.target){
31432             return;
31433         }
31434         
31435         var width = image.dom.naturalWidth || image.dom.width;
31436         var height = image.dom.naturalHeight || image.dom.height;
31437         
31438         if(!this.previewResize) {
31439             return;
31440         }
31441         
31442         if(width > height){
31443             file.target.addClass('wide');
31444             return;
31445         }
31446         
31447         file.target.addClass('tall');
31448         return;
31449         
31450     },
31451     
31452     uploadFromSource : function(file, crop)
31453     {
31454         this.xhr = new XMLHttpRequest();
31455         
31456         this.managerEl.createChild({
31457             tag : 'div',
31458             cls : 'roo-document-manager-loading',
31459             cn : [
31460                 {
31461                     tag : 'div',
31462                     tooltip : file.name,
31463                     cls : 'roo-document-manager-thumb',
31464                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31465                 }
31466             ]
31467
31468         });
31469
31470         this.xhr.open(this.method, this.url, true);
31471         
31472         var headers = {
31473             "Accept": "application/json",
31474             "Cache-Control": "no-cache",
31475             "X-Requested-With": "XMLHttpRequest"
31476         };
31477         
31478         for (var headerName in headers) {
31479             var headerValue = headers[headerName];
31480             if (headerValue) {
31481                 this.xhr.setRequestHeader(headerName, headerValue);
31482             }
31483         }
31484         
31485         var _this = this;
31486         
31487         this.xhr.onload = function()
31488         {
31489             _this.xhrOnLoad(_this.xhr);
31490         }
31491         
31492         this.xhr.onerror = function()
31493         {
31494             _this.xhrOnError(_this.xhr);
31495         }
31496         
31497         var formData = new FormData();
31498
31499         formData.append('returnHTML', 'NO');
31500         
31501         formData.append('crop', crop);
31502         
31503         if(typeof(file.filename) != 'undefined'){
31504             formData.append('filename', file.filename);
31505         }
31506         
31507         if(typeof(file.mimetype) != 'undefined'){
31508             formData.append('mimetype', file.mimetype);
31509         }
31510         
31511         Roo.log(formData);
31512         
31513         if(this.fireEvent('prepare', this, formData) != false){
31514             this.xhr.send(formData);
31515         };
31516     }
31517 });
31518
31519 /*
31520 * Licence: LGPL
31521 */
31522
31523 /**
31524  * @class Roo.bootstrap.DocumentViewer
31525  * @extends Roo.bootstrap.Component
31526  * Bootstrap DocumentViewer class
31527  * @cfg {Boolean} showDownload (true|false) show download button (default true)
31528  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31529  * 
31530  * @constructor
31531  * Create a new DocumentViewer
31532  * @param {Object} config The config object
31533  */
31534
31535 Roo.bootstrap.DocumentViewer = function(config){
31536     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31537     
31538     this.addEvents({
31539         /**
31540          * @event initial
31541          * Fire after initEvent
31542          * @param {Roo.bootstrap.DocumentViewer} this
31543          */
31544         "initial" : true,
31545         /**
31546          * @event click
31547          * Fire after click
31548          * @param {Roo.bootstrap.DocumentViewer} this
31549          */
31550         "click" : true,
31551         /**
31552          * @event download
31553          * Fire after download button
31554          * @param {Roo.bootstrap.DocumentViewer} this
31555          */
31556         "download" : true,
31557         /**
31558          * @event trash
31559          * Fire after trash button
31560          * @param {Roo.bootstrap.DocumentViewer} this
31561          */
31562         "trash" : true
31563         
31564     });
31565 };
31566
31567 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
31568     
31569     showDownload : true,
31570     
31571     showTrash : true,
31572     
31573     getAutoCreate : function()
31574     {
31575         var cfg = {
31576             tag : 'div',
31577             cls : 'roo-document-viewer',
31578             cn : [
31579                 {
31580                     tag : 'div',
31581                     cls : 'roo-document-viewer-body',
31582                     cn : [
31583                         {
31584                             tag : 'div',
31585                             cls : 'roo-document-viewer-thumb',
31586                             cn : [
31587                                 {
31588                                     tag : 'img',
31589                                     cls : 'roo-document-viewer-image'
31590                                 }
31591                             ]
31592                         }
31593                     ]
31594                 },
31595                 {
31596                     tag : 'div',
31597                     cls : 'roo-document-viewer-footer',
31598                     cn : {
31599                         tag : 'div',
31600                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31601                         cn : [
31602                             {
31603                                 tag : 'div',
31604                                 cls : 'btn-group roo-document-viewer-download',
31605                                 cn : [
31606                                     {
31607                                         tag : 'button',
31608                                         cls : 'btn btn-default',
31609                                         html : '<i class="fa fa-download"></i>'
31610                                     }
31611                                 ]
31612                             },
31613                             {
31614                                 tag : 'div',
31615                                 cls : 'btn-group roo-document-viewer-trash',
31616                                 cn : [
31617                                     {
31618                                         tag : 'button',
31619                                         cls : 'btn btn-default',
31620                                         html : '<i class="fa fa-trash"></i>'
31621                                     }
31622                                 ]
31623                             }
31624                         ]
31625                     }
31626                 }
31627             ]
31628         };
31629         
31630         return cfg;
31631     },
31632     
31633     initEvents : function()
31634     {
31635         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31636         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31637         
31638         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31639         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31640         
31641         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31642         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31643         
31644         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31645         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31646         
31647         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31648         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31649         
31650         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31651         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31652         
31653         this.bodyEl.on('click', this.onClick, this);
31654         this.downloadBtn.on('click', this.onDownload, this);
31655         this.trashBtn.on('click', this.onTrash, this);
31656         
31657         this.downloadBtn.hide();
31658         this.trashBtn.hide();
31659         
31660         if(this.showDownload){
31661             this.downloadBtn.show();
31662         }
31663         
31664         if(this.showTrash){
31665             this.trashBtn.show();
31666         }
31667         
31668         if(!this.showDownload && !this.showTrash) {
31669             this.footerEl.hide();
31670         }
31671         
31672     },
31673     
31674     initial : function()
31675     {
31676         this.fireEvent('initial', this);
31677         
31678     },
31679     
31680     onClick : function(e)
31681     {
31682         e.preventDefault();
31683         
31684         this.fireEvent('click', this);
31685     },
31686     
31687     onDownload : function(e)
31688     {
31689         e.preventDefault();
31690         
31691         this.fireEvent('download', this);
31692     },
31693     
31694     onTrash : function(e)
31695     {
31696         e.preventDefault();
31697         
31698         this.fireEvent('trash', this);
31699     }
31700     
31701 });
31702 /*
31703  * - LGPL
31704  *
31705  * nav progress bar
31706  * 
31707  */
31708
31709 /**
31710  * @class Roo.bootstrap.NavProgressBar
31711  * @extends Roo.bootstrap.Component
31712  * Bootstrap NavProgressBar class
31713  * 
31714  * @constructor
31715  * Create a new nav progress bar
31716  * @param {Object} config The config object
31717  */
31718
31719 Roo.bootstrap.NavProgressBar = function(config){
31720     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31721
31722     this.bullets = this.bullets || [];
31723    
31724 //    Roo.bootstrap.NavProgressBar.register(this);
31725      this.addEvents({
31726         /**
31727              * @event changed
31728              * Fires when the active item changes
31729              * @param {Roo.bootstrap.NavProgressBar} this
31730              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31731              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
31732          */
31733         'changed': true
31734      });
31735     
31736 };
31737
31738 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
31739     
31740     bullets : [],
31741     barItems : [],
31742     
31743     getAutoCreate : function()
31744     {
31745         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31746         
31747         cfg = {
31748             tag : 'div',
31749             cls : 'roo-navigation-bar-group',
31750             cn : [
31751                 {
31752                     tag : 'div',
31753                     cls : 'roo-navigation-top-bar'
31754                 },
31755                 {
31756                     tag : 'div',
31757                     cls : 'roo-navigation-bullets-bar',
31758                     cn : [
31759                         {
31760                             tag : 'ul',
31761                             cls : 'roo-navigation-bar'
31762                         }
31763                     ]
31764                 },
31765                 
31766                 {
31767                     tag : 'div',
31768                     cls : 'roo-navigation-bottom-bar'
31769                 }
31770             ]
31771             
31772         };
31773         
31774         return cfg;
31775         
31776     },
31777     
31778     initEvents: function() 
31779     {
31780         
31781     },
31782     
31783     onRender : function(ct, position) 
31784     {
31785         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31786         
31787         if(this.bullets.length){
31788             Roo.each(this.bullets, function(b){
31789                this.addItem(b);
31790             }, this);
31791         }
31792         
31793         this.format();
31794         
31795     },
31796     
31797     addItem : function(cfg)
31798     {
31799         var item = new Roo.bootstrap.NavProgressItem(cfg);
31800         
31801         item.parentId = this.id;
31802         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
31803         
31804         if(cfg.html){
31805             var top = new Roo.bootstrap.Element({
31806                 tag : 'div',
31807                 cls : 'roo-navigation-bar-text'
31808             });
31809             
31810             var bottom = new Roo.bootstrap.Element({
31811                 tag : 'div',
31812                 cls : 'roo-navigation-bar-text'
31813             });
31814             
31815             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
31816             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
31817             
31818             var topText = new Roo.bootstrap.Element({
31819                 tag : 'span',
31820                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
31821             });
31822             
31823             var bottomText = new Roo.bootstrap.Element({
31824                 tag : 'span',
31825                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
31826             });
31827             
31828             topText.onRender(top.el, null);
31829             bottomText.onRender(bottom.el, null);
31830             
31831             item.topEl = top;
31832             item.bottomEl = bottom;
31833         }
31834         
31835         this.barItems.push(item);
31836         
31837         return item;
31838     },
31839     
31840     getActive : function()
31841     {
31842         var active = false;
31843         
31844         Roo.each(this.barItems, function(v){
31845             
31846             if (!v.isActive()) {
31847                 return;
31848             }
31849             
31850             active = v;
31851             return false;
31852             
31853         });
31854         
31855         return active;
31856     },
31857     
31858     setActiveItem : function(item)
31859     {
31860         var prev = false;
31861         
31862         Roo.each(this.barItems, function(v){
31863             if (v.rid == item.rid) {
31864                 return ;
31865             }
31866             
31867             if (v.isActive()) {
31868                 v.setActive(false);
31869                 prev = v;
31870             }
31871         });
31872
31873         item.setActive(true);
31874         
31875         this.fireEvent('changed', this, item, prev);
31876     },
31877     
31878     getBarItem: function(rid)
31879     {
31880         var ret = false;
31881         
31882         Roo.each(this.barItems, function(e) {
31883             if (e.rid != rid) {
31884                 return;
31885             }
31886             
31887             ret =  e;
31888             return false;
31889         });
31890         
31891         return ret;
31892     },
31893     
31894     indexOfItem : function(item)
31895     {
31896         var index = false;
31897         
31898         Roo.each(this.barItems, function(v, i){
31899             
31900             if (v.rid != item.rid) {
31901                 return;
31902             }
31903             
31904             index = i;
31905             return false
31906         });
31907         
31908         return index;
31909     },
31910     
31911     setActiveNext : function()
31912     {
31913         var i = this.indexOfItem(this.getActive());
31914         
31915         if (i > this.barItems.length) {
31916             return;
31917         }
31918         
31919         this.setActiveItem(this.barItems[i+1]);
31920     },
31921     
31922     setActivePrev : function()
31923     {
31924         var i = this.indexOfItem(this.getActive());
31925         
31926         if (i  < 1) {
31927             return;
31928         }
31929         
31930         this.setActiveItem(this.barItems[i-1]);
31931     },
31932     
31933     format : function()
31934     {
31935         if(!this.barItems.length){
31936             return;
31937         }
31938      
31939         var width = 100 / this.barItems.length;
31940         
31941         Roo.each(this.barItems, function(i){
31942             i.el.setStyle('width', width + '%');
31943             i.topEl.el.setStyle('width', width + '%');
31944             i.bottomEl.el.setStyle('width', width + '%');
31945         }, this);
31946         
31947     }
31948     
31949 });
31950 /*
31951  * - LGPL
31952  *
31953  * Nav Progress Item
31954  * 
31955  */
31956
31957 /**
31958  * @class Roo.bootstrap.NavProgressItem
31959  * @extends Roo.bootstrap.Component
31960  * Bootstrap NavProgressItem class
31961  * @cfg {String} rid the reference id
31962  * @cfg {Boolean} active (true|false) Is item active default false
31963  * @cfg {Boolean} disabled (true|false) Is item active default false
31964  * @cfg {String} html
31965  * @cfg {String} position (top|bottom) text position default bottom
31966  * @cfg {String} icon show icon instead of number
31967  * 
31968  * @constructor
31969  * Create a new NavProgressItem
31970  * @param {Object} config The config object
31971  */
31972 Roo.bootstrap.NavProgressItem = function(config){
31973     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
31974     this.addEvents({
31975         // raw events
31976         /**
31977          * @event click
31978          * The raw click event for the entire grid.
31979          * @param {Roo.bootstrap.NavProgressItem} this
31980          * @param {Roo.EventObject} e
31981          */
31982         "click" : true
31983     });
31984    
31985 };
31986
31987 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
31988     
31989     rid : '',
31990     active : false,
31991     disabled : false,
31992     html : '',
31993     position : 'bottom',
31994     icon : false,
31995     
31996     getAutoCreate : function()
31997     {
31998         var iconCls = 'roo-navigation-bar-item-icon';
31999         
32000         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32001         
32002         var cfg = {
32003             tag: 'li',
32004             cls: 'roo-navigation-bar-item',
32005             cn : [
32006                 {
32007                     tag : 'i',
32008                     cls : iconCls
32009                 }
32010             ]
32011         };
32012         
32013         if(this.active){
32014             cfg.cls += ' active';
32015         }
32016         if(this.disabled){
32017             cfg.cls += ' disabled';
32018         }
32019         
32020         return cfg;
32021     },
32022     
32023     disable : function()
32024     {
32025         this.setDisabled(true);
32026     },
32027     
32028     enable : function()
32029     {
32030         this.setDisabled(false);
32031     },
32032     
32033     initEvents: function() 
32034     {
32035         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32036         
32037         this.iconEl.on('click', this.onClick, this);
32038     },
32039     
32040     onClick : function(e)
32041     {
32042         e.preventDefault();
32043         
32044         if(this.disabled){
32045             return;
32046         }
32047         
32048         if(this.fireEvent('click', this, e) === false){
32049             return;
32050         };
32051         
32052         this.parent().setActiveItem(this);
32053     },
32054     
32055     isActive: function () 
32056     {
32057         return this.active;
32058     },
32059     
32060     setActive : function(state)
32061     {
32062         if(this.active == state){
32063             return;
32064         }
32065         
32066         this.active = state;
32067         
32068         if (state) {
32069             this.el.addClass('active');
32070             return;
32071         }
32072         
32073         this.el.removeClass('active');
32074         
32075         return;
32076     },
32077     
32078     setDisabled : function(state)
32079     {
32080         if(this.disabled == state){
32081             return;
32082         }
32083         
32084         this.disabled = state;
32085         
32086         if (state) {
32087             this.el.addClass('disabled');
32088             return;
32089         }
32090         
32091         this.el.removeClass('disabled');
32092     },
32093     
32094     tooltipEl : function()
32095     {
32096         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32097     }
32098 });
32099  
32100
32101  /*
32102  * - LGPL
32103  *
32104  * FieldLabel
32105  * 
32106  */
32107
32108 /**
32109  * @class Roo.bootstrap.FieldLabel
32110  * @extends Roo.bootstrap.Component
32111  * Bootstrap FieldLabel class
32112  * @cfg {String} html contents of the element
32113  * @cfg {String} tag tag of the element default label
32114  * @cfg {String} cls class of the element
32115  * @cfg {String} target label target 
32116  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32117  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32118  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32119  * @cfg {String} iconTooltip default "This field is required"
32120  * @cfg {String} indicatorpos (left|right) default left
32121  * 
32122  * @constructor
32123  * Create a new FieldLabel
32124  * @param {Object} config The config object
32125  */
32126
32127 Roo.bootstrap.FieldLabel = function(config){
32128     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32129     
32130     this.addEvents({
32131             /**
32132              * @event invalid
32133              * Fires after the field has been marked as invalid.
32134              * @param {Roo.form.FieldLabel} this
32135              * @param {String} msg The validation message
32136              */
32137             invalid : true,
32138             /**
32139              * @event valid
32140              * Fires after the field has been validated with no errors.
32141              * @param {Roo.form.FieldLabel} this
32142              */
32143             valid : true
32144         });
32145 };
32146
32147 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32148     
32149     tag: 'label',
32150     cls: '',
32151     html: '',
32152     target: '',
32153     allowBlank : true,
32154     invalidClass : 'has-warning',
32155     validClass : 'has-success',
32156     iconTooltip : 'This field is required',
32157     indicatorpos : 'left',
32158     
32159     getAutoCreate : function(){
32160         
32161         var cls = "";
32162         if (!this.allowBlank) {
32163             cls  = "visible";
32164         }
32165         
32166         var cfg = {
32167             tag : this.tag,
32168             cls : 'roo-bootstrap-field-label ' + this.cls,
32169             for : this.target,
32170             cn : [
32171                 {
32172                     tag : 'i',
32173                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32174                     tooltip : this.iconTooltip
32175                 },
32176                 {
32177                     tag : 'span',
32178                     html : this.html
32179                 }
32180             ] 
32181         };
32182         
32183         if(this.indicatorpos == 'right'){
32184             var cfg = {
32185                 tag : this.tag,
32186                 cls : 'roo-bootstrap-field-label ' + this.cls,
32187                 for : this.target,
32188                 cn : [
32189                     {
32190                         tag : 'span',
32191                         html : this.html
32192                     },
32193                     {
32194                         tag : 'i',
32195                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32196                         tooltip : this.iconTooltip
32197                     }
32198                 ] 
32199             };
32200         }
32201         
32202         return cfg;
32203     },
32204     
32205     initEvents: function() 
32206     {
32207         Roo.bootstrap.Element.superclass.initEvents.call(this);
32208         
32209         this.indicator = this.indicatorEl();
32210         
32211         if(this.indicator){
32212             this.indicator.removeClass('visible');
32213             this.indicator.addClass('invisible');
32214         }
32215         
32216         Roo.bootstrap.FieldLabel.register(this);
32217     },
32218     
32219     indicatorEl : function()
32220     {
32221         var indicator = this.el.select('i.roo-required-indicator',true).first();
32222         
32223         if(!indicator){
32224             return false;
32225         }
32226         
32227         return indicator;
32228         
32229     },
32230     
32231     /**
32232      * Mark this field as valid
32233      */
32234     markValid : function()
32235     {
32236         if(this.indicator){
32237             this.indicator.removeClass('visible');
32238             this.indicator.addClass('invisible');
32239         }
32240         if (Roo.bootstrap.version == 3) {
32241             this.el.removeClass(this.invalidClass);
32242             this.el.addClass(this.validClass);
32243         } else {
32244             this.el.removeClass('is-invalid');
32245             this.el.addClass('is-valid');
32246         }
32247         
32248         
32249         this.fireEvent('valid', this);
32250     },
32251     
32252     /**
32253      * Mark this field as invalid
32254      * @param {String} msg The validation message
32255      */
32256     markInvalid : function(msg)
32257     {
32258         if(this.indicator){
32259             this.indicator.removeClass('invisible');
32260             this.indicator.addClass('visible');
32261         }
32262           if (Roo.bootstrap.version == 3) {
32263             this.el.removeClass(this.validClass);
32264             this.el.addClass(this.invalidClass);
32265         } else {
32266             this.el.removeClass('is-valid');
32267             this.el.addClass('is-invalid');
32268         }
32269         
32270         
32271         this.fireEvent('invalid', this, msg);
32272     }
32273     
32274    
32275 });
32276
32277 Roo.apply(Roo.bootstrap.FieldLabel, {
32278     
32279     groups: {},
32280     
32281      /**
32282     * register a FieldLabel Group
32283     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32284     */
32285     register : function(label)
32286     {
32287         if(this.groups.hasOwnProperty(label.target)){
32288             return;
32289         }
32290      
32291         this.groups[label.target] = label;
32292         
32293     },
32294     /**
32295     * fetch a FieldLabel Group based on the target
32296     * @param {string} target
32297     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32298     */
32299     get: function(target) {
32300         if (typeof(this.groups[target]) == 'undefined') {
32301             return false;
32302         }
32303         
32304         return this.groups[target] ;
32305     }
32306 });
32307
32308  
32309
32310  /*
32311  * - LGPL
32312  *
32313  * page DateSplitField.
32314  * 
32315  */
32316
32317
32318 /**
32319  * @class Roo.bootstrap.DateSplitField
32320  * @extends Roo.bootstrap.Component
32321  * Bootstrap DateSplitField class
32322  * @cfg {string} fieldLabel - the label associated
32323  * @cfg {Number} labelWidth set the width of label (0-12)
32324  * @cfg {String} labelAlign (top|left)
32325  * @cfg {Boolean} dayAllowBlank (true|false) default false
32326  * @cfg {Boolean} monthAllowBlank (true|false) default false
32327  * @cfg {Boolean} yearAllowBlank (true|false) default false
32328  * @cfg {string} dayPlaceholder 
32329  * @cfg {string} monthPlaceholder
32330  * @cfg {string} yearPlaceholder
32331  * @cfg {string} dayFormat default 'd'
32332  * @cfg {string} monthFormat default 'm'
32333  * @cfg {string} yearFormat default 'Y'
32334  * @cfg {Number} labellg set the width of label (1-12)
32335  * @cfg {Number} labelmd set the width of label (1-12)
32336  * @cfg {Number} labelsm set the width of label (1-12)
32337  * @cfg {Number} labelxs set the width of label (1-12)
32338
32339  *     
32340  * @constructor
32341  * Create a new DateSplitField
32342  * @param {Object} config The config object
32343  */
32344
32345 Roo.bootstrap.DateSplitField = function(config){
32346     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32347     
32348     this.addEvents({
32349         // raw events
32350          /**
32351          * @event years
32352          * getting the data of years
32353          * @param {Roo.bootstrap.DateSplitField} this
32354          * @param {Object} years
32355          */
32356         "years" : true,
32357         /**
32358          * @event days
32359          * getting the data of days
32360          * @param {Roo.bootstrap.DateSplitField} this
32361          * @param {Object} days
32362          */
32363         "days" : true,
32364         /**
32365          * @event invalid
32366          * Fires after the field has been marked as invalid.
32367          * @param {Roo.form.Field} this
32368          * @param {String} msg The validation message
32369          */
32370         invalid : true,
32371        /**
32372          * @event valid
32373          * Fires after the field has been validated with no errors.
32374          * @param {Roo.form.Field} this
32375          */
32376         valid : true
32377     });
32378 };
32379
32380 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32381     
32382     fieldLabel : '',
32383     labelAlign : 'top',
32384     labelWidth : 3,
32385     dayAllowBlank : false,
32386     monthAllowBlank : false,
32387     yearAllowBlank : false,
32388     dayPlaceholder : '',
32389     monthPlaceholder : '',
32390     yearPlaceholder : '',
32391     dayFormat : 'd',
32392     monthFormat : 'm',
32393     yearFormat : 'Y',
32394     isFormField : true,
32395     labellg : 0,
32396     labelmd : 0,
32397     labelsm : 0,
32398     labelxs : 0,
32399     
32400     getAutoCreate : function()
32401     {
32402         var cfg = {
32403             tag : 'div',
32404             cls : 'row roo-date-split-field-group',
32405             cn : [
32406                 {
32407                     tag : 'input',
32408                     type : 'hidden',
32409                     cls : 'form-hidden-field roo-date-split-field-group-value',
32410                     name : this.name
32411                 }
32412             ]
32413         };
32414         
32415         var labelCls = 'col-md-12';
32416         var contentCls = 'col-md-4';
32417         
32418         if(this.fieldLabel){
32419             
32420             var label = {
32421                 tag : 'div',
32422                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32423                 cn : [
32424                     {
32425                         tag : 'label',
32426                         html : this.fieldLabel
32427                     }
32428                 ]
32429             };
32430             
32431             if(this.labelAlign == 'left'){
32432             
32433                 if(this.labelWidth > 12){
32434                     label.style = "width: " + this.labelWidth + 'px';
32435                 }
32436
32437                 if(this.labelWidth < 13 && this.labelmd == 0){
32438                     this.labelmd = this.labelWidth;
32439                 }
32440
32441                 if(this.labellg > 0){
32442                     labelCls = ' col-lg-' + this.labellg;
32443                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32444                 }
32445
32446                 if(this.labelmd > 0){
32447                     labelCls = ' col-md-' + this.labelmd;
32448                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32449                 }
32450
32451                 if(this.labelsm > 0){
32452                     labelCls = ' col-sm-' + this.labelsm;
32453                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32454                 }
32455
32456                 if(this.labelxs > 0){
32457                     labelCls = ' col-xs-' + this.labelxs;
32458                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32459                 }
32460             }
32461             
32462             label.cls += ' ' + labelCls;
32463             
32464             cfg.cn.push(label);
32465         }
32466         
32467         Roo.each(['day', 'month', 'year'], function(t){
32468             cfg.cn.push({
32469                 tag : 'div',
32470                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32471             });
32472         }, this);
32473         
32474         return cfg;
32475     },
32476     
32477     inputEl: function ()
32478     {
32479         return this.el.select('.roo-date-split-field-group-value', true).first();
32480     },
32481     
32482     onRender : function(ct, position) 
32483     {
32484         var _this = this;
32485         
32486         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32487         
32488         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32489         
32490         this.dayField = new Roo.bootstrap.ComboBox({
32491             allowBlank : this.dayAllowBlank,
32492             alwaysQuery : true,
32493             displayField : 'value',
32494             editable : false,
32495             fieldLabel : '',
32496             forceSelection : true,
32497             mode : 'local',
32498             placeholder : this.dayPlaceholder,
32499             selectOnFocus : true,
32500             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32501             triggerAction : 'all',
32502             typeAhead : true,
32503             valueField : 'value',
32504             store : new Roo.data.SimpleStore({
32505                 data : (function() {    
32506                     var days = [];
32507                     _this.fireEvent('days', _this, days);
32508                     return days;
32509                 })(),
32510                 fields : [ 'value' ]
32511             }),
32512             listeners : {
32513                 select : function (_self, record, index)
32514                 {
32515                     _this.setValue(_this.getValue());
32516                 }
32517             }
32518         });
32519
32520         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32521         
32522         this.monthField = new Roo.bootstrap.MonthField({
32523             after : '<i class=\"fa fa-calendar\"></i>',
32524             allowBlank : this.monthAllowBlank,
32525             placeholder : this.monthPlaceholder,
32526             readOnly : true,
32527             listeners : {
32528                 render : function (_self)
32529                 {
32530                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
32531                         e.preventDefault();
32532                         _self.focus();
32533                     });
32534                 },
32535                 select : function (_self, oldvalue, newvalue)
32536                 {
32537                     _this.setValue(_this.getValue());
32538                 }
32539             }
32540         });
32541         
32542         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32543         
32544         this.yearField = new Roo.bootstrap.ComboBox({
32545             allowBlank : this.yearAllowBlank,
32546             alwaysQuery : true,
32547             displayField : 'value',
32548             editable : false,
32549             fieldLabel : '',
32550             forceSelection : true,
32551             mode : 'local',
32552             placeholder : this.yearPlaceholder,
32553             selectOnFocus : true,
32554             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32555             triggerAction : 'all',
32556             typeAhead : true,
32557             valueField : 'value',
32558             store : new Roo.data.SimpleStore({
32559                 data : (function() {
32560                     var years = [];
32561                     _this.fireEvent('years', _this, years);
32562                     return years;
32563                 })(),
32564                 fields : [ 'value' ]
32565             }),
32566             listeners : {
32567                 select : function (_self, record, index)
32568                 {
32569                     _this.setValue(_this.getValue());
32570                 }
32571             }
32572         });
32573
32574         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32575     },
32576     
32577     setValue : function(v, format)
32578     {
32579         this.inputEl.dom.value = v;
32580         
32581         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32582         
32583         var d = Date.parseDate(v, f);
32584         
32585         if(!d){
32586             this.validate();
32587             return;
32588         }
32589         
32590         this.setDay(d.format(this.dayFormat));
32591         this.setMonth(d.format(this.monthFormat));
32592         this.setYear(d.format(this.yearFormat));
32593         
32594         this.validate();
32595         
32596         return;
32597     },
32598     
32599     setDay : function(v)
32600     {
32601         this.dayField.setValue(v);
32602         this.inputEl.dom.value = this.getValue();
32603         this.validate();
32604         return;
32605     },
32606     
32607     setMonth : function(v)
32608     {
32609         this.monthField.setValue(v, true);
32610         this.inputEl.dom.value = this.getValue();
32611         this.validate();
32612         return;
32613     },
32614     
32615     setYear : function(v)
32616     {
32617         this.yearField.setValue(v);
32618         this.inputEl.dom.value = this.getValue();
32619         this.validate();
32620         return;
32621     },
32622     
32623     getDay : function()
32624     {
32625         return this.dayField.getValue();
32626     },
32627     
32628     getMonth : function()
32629     {
32630         return this.monthField.getValue();
32631     },
32632     
32633     getYear : function()
32634     {
32635         return this.yearField.getValue();
32636     },
32637     
32638     getValue : function()
32639     {
32640         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32641         
32642         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32643         
32644         return date;
32645     },
32646     
32647     reset : function()
32648     {
32649         this.setDay('');
32650         this.setMonth('');
32651         this.setYear('');
32652         this.inputEl.dom.value = '';
32653         this.validate();
32654         return;
32655     },
32656     
32657     validate : function()
32658     {
32659         var d = this.dayField.validate();
32660         var m = this.monthField.validate();
32661         var y = this.yearField.validate();
32662         
32663         var valid = true;
32664         
32665         if(
32666                 (!this.dayAllowBlank && !d) ||
32667                 (!this.monthAllowBlank && !m) ||
32668                 (!this.yearAllowBlank && !y)
32669         ){
32670             valid = false;
32671         }
32672         
32673         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32674             return valid;
32675         }
32676         
32677         if(valid){
32678             this.markValid();
32679             return valid;
32680         }
32681         
32682         this.markInvalid();
32683         
32684         return valid;
32685     },
32686     
32687     markValid : function()
32688     {
32689         
32690         var label = this.el.select('label', true).first();
32691         var icon = this.el.select('i.fa-star', true).first();
32692
32693         if(label && icon){
32694             icon.remove();
32695         }
32696         
32697         this.fireEvent('valid', this);
32698     },
32699     
32700      /**
32701      * Mark this field as invalid
32702      * @param {String} msg The validation message
32703      */
32704     markInvalid : function(msg)
32705     {
32706         
32707         var label = this.el.select('label', true).first();
32708         var icon = this.el.select('i.fa-star', true).first();
32709
32710         if(label && !icon){
32711             this.el.select('.roo-date-split-field-label', true).createChild({
32712                 tag : 'i',
32713                 cls : 'text-danger fa fa-lg fa-star',
32714                 tooltip : 'This field is required',
32715                 style : 'margin-right:5px;'
32716             }, label, true);
32717         }
32718         
32719         this.fireEvent('invalid', this, msg);
32720     },
32721     
32722     clearInvalid : function()
32723     {
32724         var label = this.el.select('label', true).first();
32725         var icon = this.el.select('i.fa-star', true).first();
32726
32727         if(label && icon){
32728             icon.remove();
32729         }
32730         
32731         this.fireEvent('valid', this);
32732     },
32733     
32734     getName: function()
32735     {
32736         return this.name;
32737     }
32738     
32739 });
32740
32741  /**
32742  *
32743  * This is based on 
32744  * http://masonry.desandro.com
32745  *
32746  * The idea is to render all the bricks based on vertical width...
32747  *
32748  * The original code extends 'outlayer' - we might need to use that....
32749  * 
32750  */
32751
32752
32753 /**
32754  * @class Roo.bootstrap.LayoutMasonry
32755  * @extends Roo.bootstrap.Component
32756  * Bootstrap Layout Masonry class
32757  * 
32758  * @constructor
32759  * Create a new Element
32760  * @param {Object} config The config object
32761  */
32762
32763 Roo.bootstrap.LayoutMasonry = function(config){
32764     
32765     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32766     
32767     this.bricks = [];
32768     
32769     Roo.bootstrap.LayoutMasonry.register(this);
32770     
32771     this.addEvents({
32772         // raw events
32773         /**
32774          * @event layout
32775          * Fire after layout the items
32776          * @param {Roo.bootstrap.LayoutMasonry} this
32777          * @param {Roo.EventObject} e
32778          */
32779         "layout" : true
32780     });
32781     
32782 };
32783
32784 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
32785     
32786     /**
32787      * @cfg {Boolean} isLayoutInstant = no animation?
32788      */   
32789     isLayoutInstant : false, // needed?
32790    
32791     /**
32792      * @cfg {Number} boxWidth  width of the columns
32793      */   
32794     boxWidth : 450,
32795     
32796       /**
32797      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
32798      */   
32799     boxHeight : 0,
32800     
32801     /**
32802      * @cfg {Number} padWidth padding below box..
32803      */   
32804     padWidth : 10, 
32805     
32806     /**
32807      * @cfg {Number} gutter gutter width..
32808      */   
32809     gutter : 10,
32810     
32811      /**
32812      * @cfg {Number} maxCols maximum number of columns
32813      */   
32814     
32815     maxCols: 0,
32816     
32817     /**
32818      * @cfg {Boolean} isAutoInitial defalut true
32819      */   
32820     isAutoInitial : true, 
32821     
32822     containerWidth: 0,
32823     
32824     /**
32825      * @cfg {Boolean} isHorizontal defalut false
32826      */   
32827     isHorizontal : false, 
32828
32829     currentSize : null,
32830     
32831     tag: 'div',
32832     
32833     cls: '',
32834     
32835     bricks: null, //CompositeElement
32836     
32837     cols : 1,
32838     
32839     _isLayoutInited : false,
32840     
32841 //    isAlternative : false, // only use for vertical layout...
32842     
32843     /**
32844      * @cfg {Number} alternativePadWidth padding below box..
32845      */   
32846     alternativePadWidth : 50,
32847     
32848     selectedBrick : [],
32849     
32850     getAutoCreate : function(){
32851         
32852         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
32853         
32854         var cfg = {
32855             tag: this.tag,
32856             cls: 'blog-masonary-wrapper ' + this.cls,
32857             cn : {
32858                 cls : 'mas-boxes masonary'
32859             }
32860         };
32861         
32862         return cfg;
32863     },
32864     
32865     getChildContainer: function( )
32866     {
32867         if (this.boxesEl) {
32868             return this.boxesEl;
32869         }
32870         
32871         this.boxesEl = this.el.select('.mas-boxes').first();
32872         
32873         return this.boxesEl;
32874     },
32875     
32876     
32877     initEvents : function()
32878     {
32879         var _this = this;
32880         
32881         if(this.isAutoInitial){
32882             Roo.log('hook children rendered');
32883             this.on('childrenrendered', function() {
32884                 Roo.log('children rendered');
32885                 _this.initial();
32886             } ,this);
32887         }
32888     },
32889     
32890     initial : function()
32891     {
32892         this.selectedBrick = [];
32893         
32894         this.currentSize = this.el.getBox(true);
32895         
32896         Roo.EventManager.onWindowResize(this.resize, this); 
32897
32898         if(!this.isAutoInitial){
32899             this.layout();
32900             return;
32901         }
32902         
32903         this.layout();
32904         
32905         return;
32906         //this.layout.defer(500,this);
32907         
32908     },
32909     
32910     resize : function()
32911     {
32912         var cs = this.el.getBox(true);
32913         
32914         if (
32915                 this.currentSize.width == cs.width && 
32916                 this.currentSize.x == cs.x && 
32917                 this.currentSize.height == cs.height && 
32918                 this.currentSize.y == cs.y 
32919         ) {
32920             Roo.log("no change in with or X or Y");
32921             return;
32922         }
32923         
32924         this.currentSize = cs;
32925         
32926         this.layout();
32927         
32928     },
32929     
32930     layout : function()
32931     {   
32932         this._resetLayout();
32933         
32934         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32935         
32936         this.layoutItems( isInstant );
32937       
32938         this._isLayoutInited = true;
32939         
32940         this.fireEvent('layout', this);
32941         
32942     },
32943     
32944     _resetLayout : function()
32945     {
32946         if(this.isHorizontal){
32947             this.horizontalMeasureColumns();
32948             return;
32949         }
32950         
32951         this.verticalMeasureColumns();
32952         
32953     },
32954     
32955     verticalMeasureColumns : function()
32956     {
32957         this.getContainerWidth();
32958         
32959 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
32960 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
32961 //            return;
32962 //        }
32963         
32964         var boxWidth = this.boxWidth + this.padWidth;
32965         
32966         if(this.containerWidth < this.boxWidth){
32967             boxWidth = this.containerWidth
32968         }
32969         
32970         var containerWidth = this.containerWidth;
32971         
32972         var cols = Math.floor(containerWidth / boxWidth);
32973         
32974         this.cols = Math.max( cols, 1 );
32975         
32976         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32977         
32978         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
32979         
32980         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
32981         
32982         this.colWidth = boxWidth + avail - this.padWidth;
32983         
32984         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
32985         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
32986     },
32987     
32988     horizontalMeasureColumns : function()
32989     {
32990         this.getContainerWidth();
32991         
32992         var boxWidth = this.boxWidth;
32993         
32994         if(this.containerWidth < boxWidth){
32995             boxWidth = this.containerWidth;
32996         }
32997         
32998         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
32999         
33000         this.el.setHeight(boxWidth);
33001         
33002     },
33003     
33004     getContainerWidth : function()
33005     {
33006         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33007     },
33008     
33009     layoutItems : function( isInstant )
33010     {
33011         Roo.log(this.bricks);
33012         
33013         var items = Roo.apply([], this.bricks);
33014         
33015         if(this.isHorizontal){
33016             this._horizontalLayoutItems( items , isInstant );
33017             return;
33018         }
33019         
33020 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33021 //            this._verticalAlternativeLayoutItems( items , isInstant );
33022 //            return;
33023 //        }
33024         
33025         this._verticalLayoutItems( items , isInstant );
33026         
33027     },
33028     
33029     _verticalLayoutItems : function ( items , isInstant)
33030     {
33031         if ( !items || !items.length ) {
33032             return;
33033         }
33034         
33035         var standard = [
33036             ['xs', 'xs', 'xs', 'tall'],
33037             ['xs', 'xs', 'tall'],
33038             ['xs', 'xs', 'sm'],
33039             ['xs', 'xs', 'xs'],
33040             ['xs', 'tall'],
33041             ['xs', 'sm'],
33042             ['xs', 'xs'],
33043             ['xs'],
33044             
33045             ['sm', 'xs', 'xs'],
33046             ['sm', 'xs'],
33047             ['sm'],
33048             
33049             ['tall', 'xs', 'xs', 'xs'],
33050             ['tall', 'xs', 'xs'],
33051             ['tall', 'xs'],
33052             ['tall']
33053             
33054         ];
33055         
33056         var queue = [];
33057         
33058         var boxes = [];
33059         
33060         var box = [];
33061         
33062         Roo.each(items, function(item, k){
33063             
33064             switch (item.size) {
33065                 // these layouts take up a full box,
33066                 case 'md' :
33067                 case 'md-left' :
33068                 case 'md-right' :
33069                 case 'wide' :
33070                     
33071                     if(box.length){
33072                         boxes.push(box);
33073                         box = [];
33074                     }
33075                     
33076                     boxes.push([item]);
33077                     
33078                     break;
33079                     
33080                 case 'xs' :
33081                 case 'sm' :
33082                 case 'tall' :
33083                     
33084                     box.push(item);
33085                     
33086                     break;
33087                 default :
33088                     break;
33089                     
33090             }
33091             
33092         }, this);
33093         
33094         if(box.length){
33095             boxes.push(box);
33096             box = [];
33097         }
33098         
33099         var filterPattern = function(box, length)
33100         {
33101             if(!box.length){
33102                 return;
33103             }
33104             
33105             var match = false;
33106             
33107             var pattern = box.slice(0, length);
33108             
33109             var format = [];
33110             
33111             Roo.each(pattern, function(i){
33112                 format.push(i.size);
33113             }, this);
33114             
33115             Roo.each(standard, function(s){
33116                 
33117                 if(String(s) != String(format)){
33118                     return;
33119                 }
33120                 
33121                 match = true;
33122                 return false;
33123                 
33124             }, this);
33125             
33126             if(!match && length == 1){
33127                 return;
33128             }
33129             
33130             if(!match){
33131                 filterPattern(box, length - 1);
33132                 return;
33133             }
33134                 
33135             queue.push(pattern);
33136
33137             box = box.slice(length, box.length);
33138
33139             filterPattern(box, 4);
33140
33141             return;
33142             
33143         }
33144         
33145         Roo.each(boxes, function(box, k){
33146             
33147             if(!box.length){
33148                 return;
33149             }
33150             
33151             if(box.length == 1){
33152                 queue.push(box);
33153                 return;
33154             }
33155             
33156             filterPattern(box, 4);
33157             
33158         }, this);
33159         
33160         this._processVerticalLayoutQueue( queue, isInstant );
33161         
33162     },
33163     
33164 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33165 //    {
33166 //        if ( !items || !items.length ) {
33167 //            return;
33168 //        }
33169 //
33170 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33171 //        
33172 //    },
33173     
33174     _horizontalLayoutItems : function ( items , isInstant)
33175     {
33176         if ( !items || !items.length || items.length < 3) {
33177             return;
33178         }
33179         
33180         items.reverse();
33181         
33182         var eItems = items.slice(0, 3);
33183         
33184         items = items.slice(3, items.length);
33185         
33186         var standard = [
33187             ['xs', 'xs', 'xs', 'wide'],
33188             ['xs', 'xs', 'wide'],
33189             ['xs', 'xs', 'sm'],
33190             ['xs', 'xs', 'xs'],
33191             ['xs', 'wide'],
33192             ['xs', 'sm'],
33193             ['xs', 'xs'],
33194             ['xs'],
33195             
33196             ['sm', 'xs', 'xs'],
33197             ['sm', 'xs'],
33198             ['sm'],
33199             
33200             ['wide', 'xs', 'xs', 'xs'],
33201             ['wide', 'xs', 'xs'],
33202             ['wide', 'xs'],
33203             ['wide'],
33204             
33205             ['wide-thin']
33206         ];
33207         
33208         var queue = [];
33209         
33210         var boxes = [];
33211         
33212         var box = [];
33213         
33214         Roo.each(items, function(item, k){
33215             
33216             switch (item.size) {
33217                 case 'md' :
33218                 case 'md-left' :
33219                 case 'md-right' :
33220                 case 'tall' :
33221                     
33222                     if(box.length){
33223                         boxes.push(box);
33224                         box = [];
33225                     }
33226                     
33227                     boxes.push([item]);
33228                     
33229                     break;
33230                     
33231                 case 'xs' :
33232                 case 'sm' :
33233                 case 'wide' :
33234                 case 'wide-thin' :
33235                     
33236                     box.push(item);
33237                     
33238                     break;
33239                 default :
33240                     break;
33241                     
33242             }
33243             
33244         }, this);
33245         
33246         if(box.length){
33247             boxes.push(box);
33248             box = [];
33249         }
33250         
33251         var filterPattern = function(box, length)
33252         {
33253             if(!box.length){
33254                 return;
33255             }
33256             
33257             var match = false;
33258             
33259             var pattern = box.slice(0, length);
33260             
33261             var format = [];
33262             
33263             Roo.each(pattern, function(i){
33264                 format.push(i.size);
33265             }, this);
33266             
33267             Roo.each(standard, function(s){
33268                 
33269                 if(String(s) != String(format)){
33270                     return;
33271                 }
33272                 
33273                 match = true;
33274                 return false;
33275                 
33276             }, this);
33277             
33278             if(!match && length == 1){
33279                 return;
33280             }
33281             
33282             if(!match){
33283                 filterPattern(box, length - 1);
33284                 return;
33285             }
33286                 
33287             queue.push(pattern);
33288
33289             box = box.slice(length, box.length);
33290
33291             filterPattern(box, 4);
33292
33293             return;
33294             
33295         }
33296         
33297         Roo.each(boxes, function(box, k){
33298             
33299             if(!box.length){
33300                 return;
33301             }
33302             
33303             if(box.length == 1){
33304                 queue.push(box);
33305                 return;
33306             }
33307             
33308             filterPattern(box, 4);
33309             
33310         }, this);
33311         
33312         
33313         var prune = [];
33314         
33315         var pos = this.el.getBox(true);
33316         
33317         var minX = pos.x;
33318         
33319         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33320         
33321         var hit_end = false;
33322         
33323         Roo.each(queue, function(box){
33324             
33325             if(hit_end){
33326                 
33327                 Roo.each(box, function(b){
33328                 
33329                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33330                     b.el.hide();
33331
33332                 }, this);
33333
33334                 return;
33335             }
33336             
33337             var mx = 0;
33338             
33339             Roo.each(box, function(b){
33340                 
33341                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33342                 b.el.show();
33343
33344                 mx = Math.max(mx, b.x);
33345                 
33346             }, this);
33347             
33348             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33349             
33350             if(maxX < minX){
33351                 
33352                 Roo.each(box, function(b){
33353                 
33354                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33355                     b.el.hide();
33356                     
33357                 }, this);
33358                 
33359                 hit_end = true;
33360                 
33361                 return;
33362             }
33363             
33364             prune.push(box);
33365             
33366         }, this);
33367         
33368         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33369     },
33370     
33371     /** Sets position of item in DOM
33372     * @param {Element} item
33373     * @param {Number} x - horizontal position
33374     * @param {Number} y - vertical position
33375     * @param {Boolean} isInstant - disables transitions
33376     */
33377     _processVerticalLayoutQueue : function( queue, isInstant )
33378     {
33379         var pos = this.el.getBox(true);
33380         var x = pos.x;
33381         var y = pos.y;
33382         var maxY = [];
33383         
33384         for (var i = 0; i < this.cols; i++){
33385             maxY[i] = pos.y;
33386         }
33387         
33388         Roo.each(queue, function(box, k){
33389             
33390             var col = k % this.cols;
33391             
33392             Roo.each(box, function(b,kk){
33393                 
33394                 b.el.position('absolute');
33395                 
33396                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33397                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33398                 
33399                 if(b.size == 'md-left' || b.size == 'md-right'){
33400                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33401                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33402                 }
33403                 
33404                 b.el.setWidth(width);
33405                 b.el.setHeight(height);
33406                 // iframe?
33407                 b.el.select('iframe',true).setSize(width,height);
33408                 
33409             }, this);
33410             
33411             for (var i = 0; i < this.cols; i++){
33412                 
33413                 if(maxY[i] < maxY[col]){
33414                     col = i;
33415                     continue;
33416                 }
33417                 
33418                 col = Math.min(col, i);
33419                 
33420             }
33421             
33422             x = pos.x + col * (this.colWidth + this.padWidth);
33423             
33424             y = maxY[col];
33425             
33426             var positions = [];
33427             
33428             switch (box.length){
33429                 case 1 :
33430                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33431                     break;
33432                 case 2 :
33433                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33434                     break;
33435                 case 3 :
33436                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33437                     break;
33438                 case 4 :
33439                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33440                     break;
33441                 default :
33442                     break;
33443             }
33444             
33445             Roo.each(box, function(b,kk){
33446                 
33447                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33448                 
33449                 var sz = b.el.getSize();
33450                 
33451                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33452                 
33453             }, this);
33454             
33455         }, this);
33456         
33457         var mY = 0;
33458         
33459         for (var i = 0; i < this.cols; i++){
33460             mY = Math.max(mY, maxY[i]);
33461         }
33462         
33463         this.el.setHeight(mY - pos.y);
33464         
33465     },
33466     
33467 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33468 //    {
33469 //        var pos = this.el.getBox(true);
33470 //        var x = pos.x;
33471 //        var y = pos.y;
33472 //        var maxX = pos.right;
33473 //        
33474 //        var maxHeight = 0;
33475 //        
33476 //        Roo.each(items, function(item, k){
33477 //            
33478 //            var c = k % 2;
33479 //            
33480 //            item.el.position('absolute');
33481 //                
33482 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33483 //
33484 //            item.el.setWidth(width);
33485 //
33486 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33487 //
33488 //            item.el.setHeight(height);
33489 //            
33490 //            if(c == 0){
33491 //                item.el.setXY([x, y], isInstant ? false : true);
33492 //            } else {
33493 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33494 //            }
33495 //            
33496 //            y = y + height + this.alternativePadWidth;
33497 //            
33498 //            maxHeight = maxHeight + height + this.alternativePadWidth;
33499 //            
33500 //        }, this);
33501 //        
33502 //        this.el.setHeight(maxHeight);
33503 //        
33504 //    },
33505     
33506     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33507     {
33508         var pos = this.el.getBox(true);
33509         
33510         var minX = pos.x;
33511         var minY = pos.y;
33512         
33513         var maxX = pos.right;
33514         
33515         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33516         
33517         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33518         
33519         Roo.each(queue, function(box, k){
33520             
33521             Roo.each(box, function(b, kk){
33522                 
33523                 b.el.position('absolute');
33524                 
33525                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33526                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33527                 
33528                 if(b.size == 'md-left' || b.size == 'md-right'){
33529                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33530                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33531                 }
33532                 
33533                 b.el.setWidth(width);
33534                 b.el.setHeight(height);
33535                 
33536             }, this);
33537             
33538             if(!box.length){
33539                 return;
33540             }
33541             
33542             var positions = [];
33543             
33544             switch (box.length){
33545                 case 1 :
33546                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33547                     break;
33548                 case 2 :
33549                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33550                     break;
33551                 case 3 :
33552                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33553                     break;
33554                 case 4 :
33555                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33556                     break;
33557                 default :
33558                     break;
33559             }
33560             
33561             Roo.each(box, function(b,kk){
33562                 
33563                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33564                 
33565                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33566                 
33567             }, this);
33568             
33569         }, this);
33570         
33571     },
33572     
33573     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33574     {
33575         Roo.each(eItems, function(b,k){
33576             
33577             b.size = (k == 0) ? 'sm' : 'xs';
33578             b.x = (k == 0) ? 2 : 1;
33579             b.y = (k == 0) ? 2 : 1;
33580             
33581             b.el.position('absolute');
33582             
33583             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33584                 
33585             b.el.setWidth(width);
33586             
33587             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33588             
33589             b.el.setHeight(height);
33590             
33591         }, this);
33592
33593         var positions = [];
33594         
33595         positions.push({
33596             x : maxX - this.unitWidth * 2 - this.gutter,
33597             y : minY
33598         });
33599         
33600         positions.push({
33601             x : maxX - this.unitWidth,
33602             y : minY + (this.unitWidth + this.gutter) * 2
33603         });
33604         
33605         positions.push({
33606             x : maxX - this.unitWidth * 3 - this.gutter * 2,
33607             y : minY
33608         });
33609         
33610         Roo.each(eItems, function(b,k){
33611             
33612             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33613
33614         }, this);
33615         
33616     },
33617     
33618     getVerticalOneBoxColPositions : function(x, y, box)
33619     {
33620         var pos = [];
33621         
33622         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33623         
33624         if(box[0].size == 'md-left'){
33625             rand = 0;
33626         }
33627         
33628         if(box[0].size == 'md-right'){
33629             rand = 1;
33630         }
33631         
33632         pos.push({
33633             x : x + (this.unitWidth + this.gutter) * rand,
33634             y : y
33635         });
33636         
33637         return pos;
33638     },
33639     
33640     getVerticalTwoBoxColPositions : function(x, y, box)
33641     {
33642         var pos = [];
33643         
33644         if(box[0].size == 'xs'){
33645             
33646             pos.push({
33647                 x : x,
33648                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33649             });
33650
33651             pos.push({
33652                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33653                 y : y
33654             });
33655             
33656             return pos;
33657             
33658         }
33659         
33660         pos.push({
33661             x : x,
33662             y : y
33663         });
33664
33665         pos.push({
33666             x : x + (this.unitWidth + this.gutter) * 2,
33667             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33668         });
33669         
33670         return pos;
33671         
33672     },
33673     
33674     getVerticalThreeBoxColPositions : function(x, y, box)
33675     {
33676         var pos = [];
33677         
33678         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33679             
33680             pos.push({
33681                 x : x,
33682                 y : y
33683             });
33684
33685             pos.push({
33686                 x : x + (this.unitWidth + this.gutter) * 1,
33687                 y : y
33688             });
33689             
33690             pos.push({
33691                 x : x + (this.unitWidth + this.gutter) * 2,
33692                 y : y
33693             });
33694             
33695             return pos;
33696             
33697         }
33698         
33699         if(box[0].size == 'xs' && box[1].size == 'xs'){
33700             
33701             pos.push({
33702                 x : x,
33703                 y : y
33704             });
33705
33706             pos.push({
33707                 x : x,
33708                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33709             });
33710             
33711             pos.push({
33712                 x : x + (this.unitWidth + this.gutter) * 1,
33713                 y : y
33714             });
33715             
33716             return pos;
33717             
33718         }
33719         
33720         pos.push({
33721             x : x,
33722             y : y
33723         });
33724
33725         pos.push({
33726             x : x + (this.unitWidth + this.gutter) * 2,
33727             y : y
33728         });
33729
33730         pos.push({
33731             x : x + (this.unitWidth + this.gutter) * 2,
33732             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33733         });
33734             
33735         return pos;
33736         
33737     },
33738     
33739     getVerticalFourBoxColPositions : function(x, y, box)
33740     {
33741         var pos = [];
33742         
33743         if(box[0].size == 'xs'){
33744             
33745             pos.push({
33746                 x : x,
33747                 y : y
33748             });
33749
33750             pos.push({
33751                 x : x,
33752                 y : y + (this.unitHeight + this.gutter) * 1
33753             });
33754             
33755             pos.push({
33756                 x : x,
33757                 y : y + (this.unitHeight + this.gutter) * 2
33758             });
33759             
33760             pos.push({
33761                 x : x + (this.unitWidth + this.gutter) * 1,
33762                 y : y
33763             });
33764             
33765             return pos;
33766             
33767         }
33768         
33769         pos.push({
33770             x : x,
33771             y : y
33772         });
33773
33774         pos.push({
33775             x : x + (this.unitWidth + this.gutter) * 2,
33776             y : y
33777         });
33778
33779         pos.push({
33780             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
33781             y : y + (this.unitHeight + this.gutter) * 1
33782         });
33783
33784         pos.push({
33785             x : x + (this.unitWidth + this.gutter) * 2,
33786             y : y + (this.unitWidth + this.gutter) * 2
33787         });
33788
33789         return pos;
33790         
33791     },
33792     
33793     getHorizontalOneBoxColPositions : function(maxX, minY, box)
33794     {
33795         var pos = [];
33796         
33797         if(box[0].size == 'md-left'){
33798             pos.push({
33799                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33800                 y : minY
33801             });
33802             
33803             return pos;
33804         }
33805         
33806         if(box[0].size == 'md-right'){
33807             pos.push({
33808                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33809                 y : minY + (this.unitWidth + this.gutter) * 1
33810             });
33811             
33812             return pos;
33813         }
33814         
33815         var rand = Math.floor(Math.random() * (4 - box[0].y));
33816         
33817         pos.push({
33818             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33819             y : minY + (this.unitWidth + this.gutter) * rand
33820         });
33821         
33822         return pos;
33823         
33824     },
33825     
33826     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
33827     {
33828         var pos = [];
33829         
33830         if(box[0].size == 'xs'){
33831             
33832             pos.push({
33833                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33834                 y : minY
33835             });
33836
33837             pos.push({
33838                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33839                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
33840             });
33841             
33842             return pos;
33843             
33844         }
33845         
33846         pos.push({
33847             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33848             y : minY
33849         });
33850
33851         pos.push({
33852             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33853             y : minY + (this.unitWidth + this.gutter) * 2
33854         });
33855         
33856         return pos;
33857         
33858     },
33859     
33860     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
33861     {
33862         var pos = [];
33863         
33864         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33865             
33866             pos.push({
33867                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33868                 y : minY
33869             });
33870
33871             pos.push({
33872                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33873                 y : minY + (this.unitWidth + this.gutter) * 1
33874             });
33875             
33876             pos.push({
33877                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33878                 y : minY + (this.unitWidth + this.gutter) * 2
33879             });
33880             
33881             return pos;
33882             
33883         }
33884         
33885         if(box[0].size == 'xs' && box[1].size == 'xs'){
33886             
33887             pos.push({
33888                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33889                 y : minY
33890             });
33891
33892             pos.push({
33893                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33894                 y : minY
33895             });
33896             
33897             pos.push({
33898                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33899                 y : minY + (this.unitWidth + this.gutter) * 1
33900             });
33901             
33902             return pos;
33903             
33904         }
33905         
33906         pos.push({
33907             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33908             y : minY
33909         });
33910
33911         pos.push({
33912             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33913             y : minY + (this.unitWidth + this.gutter) * 2
33914         });
33915
33916         pos.push({
33917             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33918             y : minY + (this.unitWidth + this.gutter) * 2
33919         });
33920             
33921         return pos;
33922         
33923     },
33924     
33925     getHorizontalFourBoxColPositions : function(maxX, minY, box)
33926     {
33927         var pos = [];
33928         
33929         if(box[0].size == 'xs'){
33930             
33931             pos.push({
33932                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33933                 y : minY
33934             });
33935
33936             pos.push({
33937                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33938                 y : minY
33939             });
33940             
33941             pos.push({
33942                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33943                 y : minY
33944             });
33945             
33946             pos.push({
33947                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
33948                 y : minY + (this.unitWidth + this.gutter) * 1
33949             });
33950             
33951             return pos;
33952             
33953         }
33954         
33955         pos.push({
33956             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33957             y : minY
33958         });
33959         
33960         pos.push({
33961             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33962             y : minY + (this.unitWidth + this.gutter) * 2
33963         });
33964         
33965         pos.push({
33966             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33967             y : minY + (this.unitWidth + this.gutter) * 2
33968         });
33969         
33970         pos.push({
33971             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
33972             y : minY + (this.unitWidth + this.gutter) * 2
33973         });
33974
33975         return pos;
33976         
33977     },
33978     
33979     /**
33980     * remove a Masonry Brick
33981     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
33982     */
33983     removeBrick : function(brick_id)
33984     {
33985         if (!brick_id) {
33986             return;
33987         }
33988         
33989         for (var i = 0; i<this.bricks.length; i++) {
33990             if (this.bricks[i].id == brick_id) {
33991                 this.bricks.splice(i,1);
33992                 this.el.dom.removeChild(Roo.get(brick_id).dom);
33993                 this.initial();
33994             }
33995         }
33996     },
33997     
33998     /**
33999     * adds a Masonry Brick
34000     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34001     */
34002     addBrick : function(cfg)
34003     {
34004         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34005         //this.register(cn);
34006         cn.parentId = this.id;
34007         cn.render(this.el);
34008         return cn;
34009     },
34010     
34011     /**
34012     * register a Masonry Brick
34013     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34014     */
34015     
34016     register : function(brick)
34017     {
34018         this.bricks.push(brick);
34019         brick.masonryId = this.id;
34020     },
34021     
34022     /**
34023     * clear all the Masonry Brick
34024     */
34025     clearAll : function()
34026     {
34027         this.bricks = [];
34028         //this.getChildContainer().dom.innerHTML = "";
34029         this.el.dom.innerHTML = '';
34030     },
34031     
34032     getSelected : function()
34033     {
34034         if (!this.selectedBrick) {
34035             return false;
34036         }
34037         
34038         return this.selectedBrick;
34039     }
34040 });
34041
34042 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34043     
34044     groups: {},
34045      /**
34046     * register a Masonry Layout
34047     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34048     */
34049     
34050     register : function(layout)
34051     {
34052         this.groups[layout.id] = layout;
34053     },
34054     /**
34055     * fetch a  Masonry Layout based on the masonry layout ID
34056     * @param {string} the masonry layout to add
34057     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34058     */
34059     
34060     get: function(layout_id) {
34061         if (typeof(this.groups[layout_id]) == 'undefined') {
34062             return false;
34063         }
34064         return this.groups[layout_id] ;
34065     }
34066     
34067     
34068     
34069 });
34070
34071  
34072
34073  /**
34074  *
34075  * This is based on 
34076  * http://masonry.desandro.com
34077  *
34078  * The idea is to render all the bricks based on vertical width...
34079  *
34080  * The original code extends 'outlayer' - we might need to use that....
34081  * 
34082  */
34083
34084
34085 /**
34086  * @class Roo.bootstrap.LayoutMasonryAuto
34087  * @extends Roo.bootstrap.Component
34088  * Bootstrap Layout Masonry class
34089  * 
34090  * @constructor
34091  * Create a new Element
34092  * @param {Object} config The config object
34093  */
34094
34095 Roo.bootstrap.LayoutMasonryAuto = function(config){
34096     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34097 };
34098
34099 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34100     
34101       /**
34102      * @cfg {Boolean} isFitWidth  - resize the width..
34103      */   
34104     isFitWidth : false,  // options..
34105     /**
34106      * @cfg {Boolean} isOriginLeft = left align?
34107      */   
34108     isOriginLeft : true,
34109     /**
34110      * @cfg {Boolean} isOriginTop = top align?
34111      */   
34112     isOriginTop : false,
34113     /**
34114      * @cfg {Boolean} isLayoutInstant = no animation?
34115      */   
34116     isLayoutInstant : false, // needed?
34117     /**
34118      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34119      */   
34120     isResizingContainer : true,
34121     /**
34122      * @cfg {Number} columnWidth  width of the columns 
34123      */   
34124     
34125     columnWidth : 0,
34126     
34127     /**
34128      * @cfg {Number} maxCols maximum number of columns
34129      */   
34130     
34131     maxCols: 0,
34132     /**
34133      * @cfg {Number} padHeight padding below box..
34134      */   
34135     
34136     padHeight : 10, 
34137     
34138     /**
34139      * @cfg {Boolean} isAutoInitial defalut true
34140      */   
34141     
34142     isAutoInitial : true, 
34143     
34144     // private?
34145     gutter : 0,
34146     
34147     containerWidth: 0,
34148     initialColumnWidth : 0,
34149     currentSize : null,
34150     
34151     colYs : null, // array.
34152     maxY : 0,
34153     padWidth: 10,
34154     
34155     
34156     tag: 'div',
34157     cls: '',
34158     bricks: null, //CompositeElement
34159     cols : 0, // array?
34160     // element : null, // wrapped now this.el
34161     _isLayoutInited : null, 
34162     
34163     
34164     getAutoCreate : function(){
34165         
34166         var cfg = {
34167             tag: this.tag,
34168             cls: 'blog-masonary-wrapper ' + this.cls,
34169             cn : {
34170                 cls : 'mas-boxes masonary'
34171             }
34172         };
34173         
34174         return cfg;
34175     },
34176     
34177     getChildContainer: function( )
34178     {
34179         if (this.boxesEl) {
34180             return this.boxesEl;
34181         }
34182         
34183         this.boxesEl = this.el.select('.mas-boxes').first();
34184         
34185         return this.boxesEl;
34186     },
34187     
34188     
34189     initEvents : function()
34190     {
34191         var _this = this;
34192         
34193         if(this.isAutoInitial){
34194             Roo.log('hook children rendered');
34195             this.on('childrenrendered', function() {
34196                 Roo.log('children rendered');
34197                 _this.initial();
34198             } ,this);
34199         }
34200         
34201     },
34202     
34203     initial : function()
34204     {
34205         this.reloadItems();
34206
34207         this.currentSize = this.el.getBox(true);
34208
34209         /// was window resize... - let's see if this works..
34210         Roo.EventManager.onWindowResize(this.resize, this); 
34211
34212         if(!this.isAutoInitial){
34213             this.layout();
34214             return;
34215         }
34216         
34217         this.layout.defer(500,this);
34218     },
34219     
34220     reloadItems: function()
34221     {
34222         this.bricks = this.el.select('.masonry-brick', true);
34223         
34224         this.bricks.each(function(b) {
34225             //Roo.log(b.getSize());
34226             if (!b.attr('originalwidth')) {
34227                 b.attr('originalwidth',  b.getSize().width);
34228             }
34229             
34230         });
34231         
34232         Roo.log(this.bricks.elements.length);
34233     },
34234     
34235     resize : function()
34236     {
34237         Roo.log('resize');
34238         var cs = this.el.getBox(true);
34239         
34240         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34241             Roo.log("no change in with or X");
34242             return;
34243         }
34244         this.currentSize = cs;
34245         this.layout();
34246     },
34247     
34248     layout : function()
34249     {
34250          Roo.log('layout');
34251         this._resetLayout();
34252         //this._manageStamps();
34253       
34254         // don't animate first layout
34255         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34256         this.layoutItems( isInstant );
34257       
34258         // flag for initalized
34259         this._isLayoutInited = true;
34260     },
34261     
34262     layoutItems : function( isInstant )
34263     {
34264         //var items = this._getItemsForLayout( this.items );
34265         // original code supports filtering layout items.. we just ignore it..
34266         
34267         this._layoutItems( this.bricks , isInstant );
34268       
34269         this._postLayout();
34270     },
34271     _layoutItems : function ( items , isInstant)
34272     {
34273        //this.fireEvent( 'layout', this, items );
34274     
34275
34276         if ( !items || !items.elements.length ) {
34277           // no items, emit event with empty array
34278             return;
34279         }
34280
34281         var queue = [];
34282         items.each(function(item) {
34283             Roo.log("layout item");
34284             Roo.log(item);
34285             // get x/y object from method
34286             var position = this._getItemLayoutPosition( item );
34287             // enqueue
34288             position.item = item;
34289             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34290             queue.push( position );
34291         }, this);
34292       
34293         this._processLayoutQueue( queue );
34294     },
34295     /** Sets position of item in DOM
34296     * @param {Element} item
34297     * @param {Number} x - horizontal position
34298     * @param {Number} y - vertical position
34299     * @param {Boolean} isInstant - disables transitions
34300     */
34301     _processLayoutQueue : function( queue )
34302     {
34303         for ( var i=0, len = queue.length; i < len; i++ ) {
34304             var obj = queue[i];
34305             obj.item.position('absolute');
34306             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34307         }
34308     },
34309       
34310     
34311     /**
34312     * Any logic you want to do after each layout,
34313     * i.e. size the container
34314     */
34315     _postLayout : function()
34316     {
34317         this.resizeContainer();
34318     },
34319     
34320     resizeContainer : function()
34321     {
34322         if ( !this.isResizingContainer ) {
34323             return;
34324         }
34325         var size = this._getContainerSize();
34326         if ( size ) {
34327             this.el.setSize(size.width,size.height);
34328             this.boxesEl.setSize(size.width,size.height);
34329         }
34330     },
34331     
34332     
34333     
34334     _resetLayout : function()
34335     {
34336         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34337         this.colWidth = this.el.getWidth();
34338         //this.gutter = this.el.getWidth(); 
34339         
34340         this.measureColumns();
34341
34342         // reset column Y
34343         var i = this.cols;
34344         this.colYs = [];
34345         while (i--) {
34346             this.colYs.push( 0 );
34347         }
34348     
34349         this.maxY = 0;
34350     },
34351
34352     measureColumns : function()
34353     {
34354         this.getContainerWidth();
34355       // if columnWidth is 0, default to outerWidth of first item
34356         if ( !this.columnWidth ) {
34357             var firstItem = this.bricks.first();
34358             Roo.log(firstItem);
34359             this.columnWidth  = this.containerWidth;
34360             if (firstItem && firstItem.attr('originalwidth') ) {
34361                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34362             }
34363             // columnWidth fall back to item of first element
34364             Roo.log("set column width?");
34365                         this.initialColumnWidth = this.columnWidth  ;
34366
34367             // if first elem has no width, default to size of container
34368             
34369         }
34370         
34371         
34372         if (this.initialColumnWidth) {
34373             this.columnWidth = this.initialColumnWidth;
34374         }
34375         
34376         
34377             
34378         // column width is fixed at the top - however if container width get's smaller we should
34379         // reduce it...
34380         
34381         // this bit calcs how man columns..
34382             
34383         var columnWidth = this.columnWidth += this.gutter;
34384       
34385         // calculate columns
34386         var containerWidth = this.containerWidth + this.gutter;
34387         
34388         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34389         // fix rounding errors, typically with gutters
34390         var excess = columnWidth - containerWidth % columnWidth;
34391         
34392         
34393         // if overshoot is less than a pixel, round up, otherwise floor it
34394         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34395         cols = Math[ mathMethod ]( cols );
34396         this.cols = Math.max( cols, 1 );
34397         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34398         
34399          // padding positioning..
34400         var totalColWidth = this.cols * this.columnWidth;
34401         var padavail = this.containerWidth - totalColWidth;
34402         // so for 2 columns - we need 3 'pads'
34403         
34404         var padNeeded = (1+this.cols) * this.padWidth;
34405         
34406         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34407         
34408         this.columnWidth += padExtra
34409         //this.padWidth = Math.floor(padavail /  ( this.cols));
34410         
34411         // adjust colum width so that padding is fixed??
34412         
34413         // we have 3 columns ... total = width * 3
34414         // we have X left over... that should be used by 
34415         
34416         //if (this.expandC) {
34417             
34418         //}
34419         
34420         
34421         
34422     },
34423     
34424     getContainerWidth : function()
34425     {
34426        /* // container is parent if fit width
34427         var container = this.isFitWidth ? this.element.parentNode : this.element;
34428         // check that this.size and size are there
34429         // IE8 triggers resize on body size change, so they might not be
34430         
34431         var size = getSize( container );  //FIXME
34432         this.containerWidth = size && size.innerWidth; //FIXME
34433         */
34434          
34435         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34436         
34437     },
34438     
34439     _getItemLayoutPosition : function( item )  // what is item?
34440     {
34441         // we resize the item to our columnWidth..
34442       
34443         item.setWidth(this.columnWidth);
34444         item.autoBoxAdjust  = false;
34445         
34446         var sz = item.getSize();
34447  
34448         // how many columns does this brick span
34449         var remainder = this.containerWidth % this.columnWidth;
34450         
34451         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34452         // round if off by 1 pixel, otherwise use ceil
34453         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34454         colSpan = Math.min( colSpan, this.cols );
34455         
34456         // normally this should be '1' as we dont' currently allow multi width columns..
34457         
34458         var colGroup = this._getColGroup( colSpan );
34459         // get the minimum Y value from the columns
34460         var minimumY = Math.min.apply( Math, colGroup );
34461         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34462         
34463         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34464          
34465         // position the brick
34466         var position = {
34467             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34468             y: this.currentSize.y + minimumY + this.padHeight
34469         };
34470         
34471         Roo.log(position);
34472         // apply setHeight to necessary columns
34473         var setHeight = minimumY + sz.height + this.padHeight;
34474         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34475         
34476         var setSpan = this.cols + 1 - colGroup.length;
34477         for ( var i = 0; i < setSpan; i++ ) {
34478           this.colYs[ shortColIndex + i ] = setHeight ;
34479         }
34480       
34481         return position;
34482     },
34483     
34484     /**
34485      * @param {Number} colSpan - number of columns the element spans
34486      * @returns {Array} colGroup
34487      */
34488     _getColGroup : function( colSpan )
34489     {
34490         if ( colSpan < 2 ) {
34491           // if brick spans only one column, use all the column Ys
34492           return this.colYs;
34493         }
34494       
34495         var colGroup = [];
34496         // how many different places could this brick fit horizontally
34497         var groupCount = this.cols + 1 - colSpan;
34498         // for each group potential horizontal position
34499         for ( var i = 0; i < groupCount; i++ ) {
34500           // make an array of colY values for that one group
34501           var groupColYs = this.colYs.slice( i, i + colSpan );
34502           // and get the max value of the array
34503           colGroup[i] = Math.max.apply( Math, groupColYs );
34504         }
34505         return colGroup;
34506     },
34507     /*
34508     _manageStamp : function( stamp )
34509     {
34510         var stampSize =  stamp.getSize();
34511         var offset = stamp.getBox();
34512         // get the columns that this stamp affects
34513         var firstX = this.isOriginLeft ? offset.x : offset.right;
34514         var lastX = firstX + stampSize.width;
34515         var firstCol = Math.floor( firstX / this.columnWidth );
34516         firstCol = Math.max( 0, firstCol );
34517         
34518         var lastCol = Math.floor( lastX / this.columnWidth );
34519         // lastCol should not go over if multiple of columnWidth #425
34520         lastCol -= lastX % this.columnWidth ? 0 : 1;
34521         lastCol = Math.min( this.cols - 1, lastCol );
34522         
34523         // set colYs to bottom of the stamp
34524         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34525             stampSize.height;
34526             
34527         for ( var i = firstCol; i <= lastCol; i++ ) {
34528           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34529         }
34530     },
34531     */
34532     
34533     _getContainerSize : function()
34534     {
34535         this.maxY = Math.max.apply( Math, this.colYs );
34536         var size = {
34537             height: this.maxY
34538         };
34539       
34540         if ( this.isFitWidth ) {
34541             size.width = this._getContainerFitWidth();
34542         }
34543       
34544         return size;
34545     },
34546     
34547     _getContainerFitWidth : function()
34548     {
34549         var unusedCols = 0;
34550         // count unused columns
34551         var i = this.cols;
34552         while ( --i ) {
34553           if ( this.colYs[i] !== 0 ) {
34554             break;
34555           }
34556           unusedCols++;
34557         }
34558         // fit container to columns that have been used
34559         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34560     },
34561     
34562     needsResizeLayout : function()
34563     {
34564         var previousWidth = this.containerWidth;
34565         this.getContainerWidth();
34566         return previousWidth !== this.containerWidth;
34567     }
34568  
34569 });
34570
34571  
34572
34573  /*
34574  * - LGPL
34575  *
34576  * element
34577  * 
34578  */
34579
34580 /**
34581  * @class Roo.bootstrap.MasonryBrick
34582  * @extends Roo.bootstrap.Component
34583  * Bootstrap MasonryBrick class
34584  * 
34585  * @constructor
34586  * Create a new MasonryBrick
34587  * @param {Object} config The config object
34588  */
34589
34590 Roo.bootstrap.MasonryBrick = function(config){
34591     
34592     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34593     
34594     Roo.bootstrap.MasonryBrick.register(this);
34595     
34596     this.addEvents({
34597         // raw events
34598         /**
34599          * @event click
34600          * When a MasonryBrick is clcik
34601          * @param {Roo.bootstrap.MasonryBrick} this
34602          * @param {Roo.EventObject} e
34603          */
34604         "click" : true
34605     });
34606 };
34607
34608 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
34609     
34610     /**
34611      * @cfg {String} title
34612      */   
34613     title : '',
34614     /**
34615      * @cfg {String} html
34616      */   
34617     html : '',
34618     /**
34619      * @cfg {String} bgimage
34620      */   
34621     bgimage : '',
34622     /**
34623      * @cfg {String} videourl
34624      */   
34625     videourl : '',
34626     /**
34627      * @cfg {String} cls
34628      */   
34629     cls : '',
34630     /**
34631      * @cfg {String} href
34632      */   
34633     href : '',
34634     /**
34635      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34636      */   
34637     size : 'xs',
34638     
34639     /**
34640      * @cfg {String} placetitle (center|bottom)
34641      */   
34642     placetitle : '',
34643     
34644     /**
34645      * @cfg {Boolean} isFitContainer defalut true
34646      */   
34647     isFitContainer : true, 
34648     
34649     /**
34650      * @cfg {Boolean} preventDefault defalut false
34651      */   
34652     preventDefault : false, 
34653     
34654     /**
34655      * @cfg {Boolean} inverse defalut false
34656      */   
34657     maskInverse : false, 
34658     
34659     getAutoCreate : function()
34660     {
34661         if(!this.isFitContainer){
34662             return this.getSplitAutoCreate();
34663         }
34664         
34665         var cls = 'masonry-brick masonry-brick-full';
34666         
34667         if(this.href.length){
34668             cls += ' masonry-brick-link';
34669         }
34670         
34671         if(this.bgimage.length){
34672             cls += ' masonry-brick-image';
34673         }
34674         
34675         if(this.maskInverse){
34676             cls += ' mask-inverse';
34677         }
34678         
34679         if(!this.html.length && !this.maskInverse && !this.videourl.length){
34680             cls += ' enable-mask';
34681         }
34682         
34683         if(this.size){
34684             cls += ' masonry-' + this.size + '-brick';
34685         }
34686         
34687         if(this.placetitle.length){
34688             
34689             switch (this.placetitle) {
34690                 case 'center' :
34691                     cls += ' masonry-center-title';
34692                     break;
34693                 case 'bottom' :
34694                     cls += ' masonry-bottom-title';
34695                     break;
34696                 default:
34697                     break;
34698             }
34699             
34700         } else {
34701             if(!this.html.length && !this.bgimage.length){
34702                 cls += ' masonry-center-title';
34703             }
34704
34705             if(!this.html.length && this.bgimage.length){
34706                 cls += ' masonry-bottom-title';
34707             }
34708         }
34709         
34710         if(this.cls){
34711             cls += ' ' + this.cls;
34712         }
34713         
34714         var cfg = {
34715             tag: (this.href.length) ? 'a' : 'div',
34716             cls: cls,
34717             cn: [
34718                 {
34719                     tag: 'div',
34720                     cls: 'masonry-brick-mask'
34721                 },
34722                 {
34723                     tag: 'div',
34724                     cls: 'masonry-brick-paragraph',
34725                     cn: []
34726                 }
34727             ]
34728         };
34729         
34730         if(this.href.length){
34731             cfg.href = this.href;
34732         }
34733         
34734         var cn = cfg.cn[1].cn;
34735         
34736         if(this.title.length){
34737             cn.push({
34738                 tag: 'h4',
34739                 cls: 'masonry-brick-title',
34740                 html: this.title
34741             });
34742         }
34743         
34744         if(this.html.length){
34745             cn.push({
34746                 tag: 'p',
34747                 cls: 'masonry-brick-text',
34748                 html: this.html
34749             });
34750         }
34751         
34752         if (!this.title.length && !this.html.length) {
34753             cfg.cn[1].cls += ' hide';
34754         }
34755         
34756         if(this.bgimage.length){
34757             cfg.cn.push({
34758                 tag: 'img',
34759                 cls: 'masonry-brick-image-view',
34760                 src: this.bgimage
34761             });
34762         }
34763         
34764         if(this.videourl.length){
34765             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34766             // youtube support only?
34767             cfg.cn.push({
34768                 tag: 'iframe',
34769                 cls: 'masonry-brick-image-view',
34770                 src: vurl,
34771                 frameborder : 0,
34772                 allowfullscreen : true
34773             });
34774         }
34775         
34776         return cfg;
34777         
34778     },
34779     
34780     getSplitAutoCreate : function()
34781     {
34782         var cls = 'masonry-brick masonry-brick-split';
34783         
34784         if(this.href.length){
34785             cls += ' masonry-brick-link';
34786         }
34787         
34788         if(this.bgimage.length){
34789             cls += ' masonry-brick-image';
34790         }
34791         
34792         if(this.size){
34793             cls += ' masonry-' + this.size + '-brick';
34794         }
34795         
34796         switch (this.placetitle) {
34797             case 'center' :
34798                 cls += ' masonry-center-title';
34799                 break;
34800             case 'bottom' :
34801                 cls += ' masonry-bottom-title';
34802                 break;
34803             default:
34804                 if(!this.bgimage.length){
34805                     cls += ' masonry-center-title';
34806                 }
34807
34808                 if(this.bgimage.length){
34809                     cls += ' masonry-bottom-title';
34810                 }
34811                 break;
34812         }
34813         
34814         if(this.cls){
34815             cls += ' ' + this.cls;
34816         }
34817         
34818         var cfg = {
34819             tag: (this.href.length) ? 'a' : 'div',
34820             cls: cls,
34821             cn: [
34822                 {
34823                     tag: 'div',
34824                     cls: 'masonry-brick-split-head',
34825                     cn: [
34826                         {
34827                             tag: 'div',
34828                             cls: 'masonry-brick-paragraph',
34829                             cn: []
34830                         }
34831                     ]
34832                 },
34833                 {
34834                     tag: 'div',
34835                     cls: 'masonry-brick-split-body',
34836                     cn: []
34837                 }
34838             ]
34839         };
34840         
34841         if(this.href.length){
34842             cfg.href = this.href;
34843         }
34844         
34845         if(this.title.length){
34846             cfg.cn[0].cn[0].cn.push({
34847                 tag: 'h4',
34848                 cls: 'masonry-brick-title',
34849                 html: this.title
34850             });
34851         }
34852         
34853         if(this.html.length){
34854             cfg.cn[1].cn.push({
34855                 tag: 'p',
34856                 cls: 'masonry-brick-text',
34857                 html: this.html
34858             });
34859         }
34860
34861         if(this.bgimage.length){
34862             cfg.cn[0].cn.push({
34863                 tag: 'img',
34864                 cls: 'masonry-brick-image-view',
34865                 src: this.bgimage
34866             });
34867         }
34868         
34869         if(this.videourl.length){
34870             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34871             // youtube support only?
34872             cfg.cn[0].cn.cn.push({
34873                 tag: 'iframe',
34874                 cls: 'masonry-brick-image-view',
34875                 src: vurl,
34876                 frameborder : 0,
34877                 allowfullscreen : true
34878             });
34879         }
34880         
34881         return cfg;
34882     },
34883     
34884     initEvents: function() 
34885     {
34886         switch (this.size) {
34887             case 'xs' :
34888                 this.x = 1;
34889                 this.y = 1;
34890                 break;
34891             case 'sm' :
34892                 this.x = 2;
34893                 this.y = 2;
34894                 break;
34895             case 'md' :
34896             case 'md-left' :
34897             case 'md-right' :
34898                 this.x = 3;
34899                 this.y = 3;
34900                 break;
34901             case 'tall' :
34902                 this.x = 2;
34903                 this.y = 3;
34904                 break;
34905             case 'wide' :
34906                 this.x = 3;
34907                 this.y = 2;
34908                 break;
34909             case 'wide-thin' :
34910                 this.x = 3;
34911                 this.y = 1;
34912                 break;
34913                         
34914             default :
34915                 break;
34916         }
34917         
34918         if(Roo.isTouch){
34919             this.el.on('touchstart', this.onTouchStart, this);
34920             this.el.on('touchmove', this.onTouchMove, this);
34921             this.el.on('touchend', this.onTouchEnd, this);
34922             this.el.on('contextmenu', this.onContextMenu, this);
34923         } else {
34924             this.el.on('mouseenter'  ,this.enter, this);
34925             this.el.on('mouseleave', this.leave, this);
34926             this.el.on('click', this.onClick, this);
34927         }
34928         
34929         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
34930             this.parent().bricks.push(this);   
34931         }
34932         
34933     },
34934     
34935     onClick: function(e, el)
34936     {
34937         var time = this.endTimer - this.startTimer;
34938         // Roo.log(e.preventDefault());
34939         if(Roo.isTouch){
34940             if(time > 1000){
34941                 e.preventDefault();
34942                 return;
34943             }
34944         }
34945         
34946         if(!this.preventDefault){
34947             return;
34948         }
34949         
34950         e.preventDefault();
34951         
34952         if (this.activeClass != '') {
34953             this.selectBrick();
34954         }
34955         
34956         this.fireEvent('click', this, e);
34957     },
34958     
34959     enter: function(e, el)
34960     {
34961         e.preventDefault();
34962         
34963         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
34964             return;
34965         }
34966         
34967         if(this.bgimage.length && this.html.length){
34968             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
34969         }
34970     },
34971     
34972     leave: function(e, el)
34973     {
34974         e.preventDefault();
34975         
34976         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
34977             return;
34978         }
34979         
34980         if(this.bgimage.length && this.html.length){
34981             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
34982         }
34983     },
34984     
34985     onTouchStart: function(e, el)
34986     {
34987 //        e.preventDefault();
34988         
34989         this.touchmoved = false;
34990         
34991         if(!this.isFitContainer){
34992             return;
34993         }
34994         
34995         if(!this.bgimage.length || !this.html.length){
34996             return;
34997         }
34998         
34999         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35000         
35001         this.timer = new Date().getTime();
35002         
35003     },
35004     
35005     onTouchMove: function(e, el)
35006     {
35007         this.touchmoved = true;
35008     },
35009     
35010     onContextMenu : function(e,el)
35011     {
35012         e.preventDefault();
35013         e.stopPropagation();
35014         return false;
35015     },
35016     
35017     onTouchEnd: function(e, el)
35018     {
35019 //        e.preventDefault();
35020         
35021         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35022         
35023             this.leave(e,el);
35024             
35025             return;
35026         }
35027         
35028         if(!this.bgimage.length || !this.html.length){
35029             
35030             if(this.href.length){
35031                 window.location.href = this.href;
35032             }
35033             
35034             return;
35035         }
35036         
35037         if(!this.isFitContainer){
35038             return;
35039         }
35040         
35041         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35042         
35043         window.location.href = this.href;
35044     },
35045     
35046     //selection on single brick only
35047     selectBrick : function() {
35048         
35049         if (!this.parentId) {
35050             return;
35051         }
35052         
35053         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35054         var index = m.selectedBrick.indexOf(this.id);
35055         
35056         if ( index > -1) {
35057             m.selectedBrick.splice(index,1);
35058             this.el.removeClass(this.activeClass);
35059             return;
35060         }
35061         
35062         for(var i = 0; i < m.selectedBrick.length; i++) {
35063             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35064             b.el.removeClass(b.activeClass);
35065         }
35066         
35067         m.selectedBrick = [];
35068         
35069         m.selectedBrick.push(this.id);
35070         this.el.addClass(this.activeClass);
35071         return;
35072     },
35073     
35074     isSelected : function(){
35075         return this.el.hasClass(this.activeClass);
35076         
35077     }
35078 });
35079
35080 Roo.apply(Roo.bootstrap.MasonryBrick, {
35081     
35082     //groups: {},
35083     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35084      /**
35085     * register a Masonry Brick
35086     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35087     */
35088     
35089     register : function(brick)
35090     {
35091         //this.groups[brick.id] = brick;
35092         this.groups.add(brick.id, brick);
35093     },
35094     /**
35095     * fetch a  masonry brick based on the masonry brick ID
35096     * @param {string} the masonry brick to add
35097     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35098     */
35099     
35100     get: function(brick_id) 
35101     {
35102         // if (typeof(this.groups[brick_id]) == 'undefined') {
35103         //     return false;
35104         // }
35105         // return this.groups[brick_id] ;
35106         
35107         if(this.groups.key(brick_id)) {
35108             return this.groups.key(brick_id);
35109         }
35110         
35111         return false;
35112     }
35113     
35114     
35115     
35116 });
35117
35118  /*
35119  * - LGPL
35120  *
35121  * element
35122  * 
35123  */
35124
35125 /**
35126  * @class Roo.bootstrap.Brick
35127  * @extends Roo.bootstrap.Component
35128  * Bootstrap Brick class
35129  * 
35130  * @constructor
35131  * Create a new Brick
35132  * @param {Object} config The config object
35133  */
35134
35135 Roo.bootstrap.Brick = function(config){
35136     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35137     
35138     this.addEvents({
35139         // raw events
35140         /**
35141          * @event click
35142          * When a Brick is click
35143          * @param {Roo.bootstrap.Brick} this
35144          * @param {Roo.EventObject} e
35145          */
35146         "click" : true
35147     });
35148 };
35149
35150 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35151     
35152     /**
35153      * @cfg {String} title
35154      */   
35155     title : '',
35156     /**
35157      * @cfg {String} html
35158      */   
35159     html : '',
35160     /**
35161      * @cfg {String} bgimage
35162      */   
35163     bgimage : '',
35164     /**
35165      * @cfg {String} cls
35166      */   
35167     cls : '',
35168     /**
35169      * @cfg {String} href
35170      */   
35171     href : '',
35172     /**
35173      * @cfg {String} video
35174      */   
35175     video : '',
35176     /**
35177      * @cfg {Boolean} square
35178      */   
35179     square : true,
35180     
35181     getAutoCreate : function()
35182     {
35183         var cls = 'roo-brick';
35184         
35185         if(this.href.length){
35186             cls += ' roo-brick-link';
35187         }
35188         
35189         if(this.bgimage.length){
35190             cls += ' roo-brick-image';
35191         }
35192         
35193         if(!this.html.length && !this.bgimage.length){
35194             cls += ' roo-brick-center-title';
35195         }
35196         
35197         if(!this.html.length && this.bgimage.length){
35198             cls += ' roo-brick-bottom-title';
35199         }
35200         
35201         if(this.cls){
35202             cls += ' ' + this.cls;
35203         }
35204         
35205         var cfg = {
35206             tag: (this.href.length) ? 'a' : 'div',
35207             cls: cls,
35208             cn: [
35209                 {
35210                     tag: 'div',
35211                     cls: 'roo-brick-paragraph',
35212                     cn: []
35213                 }
35214             ]
35215         };
35216         
35217         if(this.href.length){
35218             cfg.href = this.href;
35219         }
35220         
35221         var cn = cfg.cn[0].cn;
35222         
35223         if(this.title.length){
35224             cn.push({
35225                 tag: 'h4',
35226                 cls: 'roo-brick-title',
35227                 html: this.title
35228             });
35229         }
35230         
35231         if(this.html.length){
35232             cn.push({
35233                 tag: 'p',
35234                 cls: 'roo-brick-text',
35235                 html: this.html
35236             });
35237         } else {
35238             cn.cls += ' hide';
35239         }
35240         
35241         if(this.bgimage.length){
35242             cfg.cn.push({
35243                 tag: 'img',
35244                 cls: 'roo-brick-image-view',
35245                 src: this.bgimage
35246             });
35247         }
35248         
35249         return cfg;
35250     },
35251     
35252     initEvents: function() 
35253     {
35254         if(this.title.length || this.html.length){
35255             this.el.on('mouseenter'  ,this.enter, this);
35256             this.el.on('mouseleave', this.leave, this);
35257         }
35258         
35259         Roo.EventManager.onWindowResize(this.resize, this); 
35260         
35261         if(this.bgimage.length){
35262             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35263             this.imageEl.on('load', this.onImageLoad, this);
35264             return;
35265         }
35266         
35267         this.resize();
35268     },
35269     
35270     onImageLoad : function()
35271     {
35272         this.resize();
35273     },
35274     
35275     resize : function()
35276     {
35277         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35278         
35279         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35280         
35281         if(this.bgimage.length){
35282             var image = this.el.select('.roo-brick-image-view', true).first();
35283             
35284             image.setWidth(paragraph.getWidth());
35285             
35286             if(this.square){
35287                 image.setHeight(paragraph.getWidth());
35288             }
35289             
35290             this.el.setHeight(image.getHeight());
35291             paragraph.setHeight(image.getHeight());
35292             
35293         }
35294         
35295     },
35296     
35297     enter: function(e, el)
35298     {
35299         e.preventDefault();
35300         
35301         if(this.bgimage.length){
35302             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35303             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35304         }
35305     },
35306     
35307     leave: function(e, el)
35308     {
35309         e.preventDefault();
35310         
35311         if(this.bgimage.length){
35312             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35313             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35314         }
35315     }
35316     
35317 });
35318
35319  
35320
35321  /*
35322  * - LGPL
35323  *
35324  * Number field 
35325  */
35326
35327 /**
35328  * @class Roo.bootstrap.NumberField
35329  * @extends Roo.bootstrap.Input
35330  * Bootstrap NumberField class
35331  * 
35332  * 
35333  * 
35334  * 
35335  * @constructor
35336  * Create a new NumberField
35337  * @param {Object} config The config object
35338  */
35339
35340 Roo.bootstrap.NumberField = function(config){
35341     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35342 };
35343
35344 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35345     
35346     /**
35347      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35348      */
35349     allowDecimals : true,
35350     /**
35351      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35352      */
35353     decimalSeparator : ".",
35354     /**
35355      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35356      */
35357     decimalPrecision : 2,
35358     /**
35359      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35360      */
35361     allowNegative : true,
35362     
35363     /**
35364      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35365      */
35366     allowZero: true,
35367     /**
35368      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35369      */
35370     minValue : Number.NEGATIVE_INFINITY,
35371     /**
35372      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35373      */
35374     maxValue : Number.MAX_VALUE,
35375     /**
35376      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35377      */
35378     minText : "The minimum value for this field is {0}",
35379     /**
35380      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35381      */
35382     maxText : "The maximum value for this field is {0}",
35383     /**
35384      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35385      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35386      */
35387     nanText : "{0} is not a valid number",
35388     /**
35389      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35390      */
35391     thousandsDelimiter : false,
35392     /**
35393      * @cfg {String} valueAlign alignment of value
35394      */
35395     valueAlign : "left",
35396
35397     getAutoCreate : function()
35398     {
35399         var hiddenInput = {
35400             tag: 'input',
35401             type: 'hidden',
35402             id: Roo.id(),
35403             cls: 'hidden-number-input'
35404         };
35405         
35406         if (this.name) {
35407             hiddenInput.name = this.name;
35408         }
35409         
35410         this.name = '';
35411         
35412         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35413         
35414         this.name = hiddenInput.name;
35415         
35416         if(cfg.cn.length > 0) {
35417             cfg.cn.push(hiddenInput);
35418         }
35419         
35420         return cfg;
35421     },
35422
35423     // private
35424     initEvents : function()
35425     {   
35426         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35427         
35428         var allowed = "0123456789";
35429         
35430         if(this.allowDecimals){
35431             allowed += this.decimalSeparator;
35432         }
35433         
35434         if(this.allowNegative){
35435             allowed += "-";
35436         }
35437         
35438         if(this.thousandsDelimiter) {
35439             allowed += ",";
35440         }
35441         
35442         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35443         
35444         var keyPress = function(e){
35445             
35446             var k = e.getKey();
35447             
35448             var c = e.getCharCode();
35449             
35450             if(
35451                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35452                     allowed.indexOf(String.fromCharCode(c)) === -1
35453             ){
35454                 e.stopEvent();
35455                 return;
35456             }
35457             
35458             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35459                 return;
35460             }
35461             
35462             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35463                 e.stopEvent();
35464             }
35465         };
35466         
35467         this.el.on("keypress", keyPress, this);
35468     },
35469     
35470     validateValue : function(value)
35471     {
35472         
35473         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35474             return false;
35475         }
35476         
35477         var num = this.parseValue(value);
35478         
35479         if(isNaN(num)){
35480             this.markInvalid(String.format(this.nanText, value));
35481             return false;
35482         }
35483         
35484         if(num < this.minValue){
35485             this.markInvalid(String.format(this.minText, this.minValue));
35486             return false;
35487         }
35488         
35489         if(num > this.maxValue){
35490             this.markInvalid(String.format(this.maxText, this.maxValue));
35491             return false;
35492         }
35493         
35494         return true;
35495     },
35496
35497     getValue : function()
35498     {
35499         var v = this.hiddenEl().getValue();
35500         
35501         return this.fixPrecision(this.parseValue(v));
35502     },
35503
35504     parseValue : function(value)
35505     {
35506         if(this.thousandsDelimiter) {
35507             value += "";
35508             r = new RegExp(",", "g");
35509             value = value.replace(r, "");
35510         }
35511         
35512         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35513         return isNaN(value) ? '' : value;
35514     },
35515
35516     fixPrecision : function(value)
35517     {
35518         if(this.thousandsDelimiter) {
35519             value += "";
35520             r = new RegExp(",", "g");
35521             value = value.replace(r, "");
35522         }
35523         
35524         var nan = isNaN(value);
35525         
35526         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35527             return nan ? '' : value;
35528         }
35529         return parseFloat(value).toFixed(this.decimalPrecision);
35530     },
35531
35532     setValue : function(v)
35533     {
35534         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35535         
35536         this.value = v;
35537         
35538         if(this.rendered){
35539             
35540             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35541             
35542             this.inputEl().dom.value = (v == '') ? '' :
35543                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35544             
35545             if(!this.allowZero && v === '0') {
35546                 this.hiddenEl().dom.value = '';
35547                 this.inputEl().dom.value = '';
35548             }
35549             
35550             this.validate();
35551         }
35552     },
35553
35554     decimalPrecisionFcn : function(v)
35555     {
35556         return Math.floor(v);
35557     },
35558
35559     beforeBlur : function()
35560     {
35561         var v = this.parseValue(this.getRawValue());
35562         
35563         if(v || v === 0 || v === ''){
35564             this.setValue(v);
35565         }
35566     },
35567     
35568     hiddenEl : function()
35569     {
35570         return this.el.select('input.hidden-number-input',true).first();
35571     }
35572     
35573 });
35574
35575  
35576
35577 /*
35578 * Licence: LGPL
35579 */
35580
35581 /**
35582  * @class Roo.bootstrap.DocumentSlider
35583  * @extends Roo.bootstrap.Component
35584  * Bootstrap DocumentSlider class
35585  * 
35586  * @constructor
35587  * Create a new DocumentViewer
35588  * @param {Object} config The config object
35589  */
35590
35591 Roo.bootstrap.DocumentSlider = function(config){
35592     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35593     
35594     this.files = [];
35595     
35596     this.addEvents({
35597         /**
35598          * @event initial
35599          * Fire after initEvent
35600          * @param {Roo.bootstrap.DocumentSlider} this
35601          */
35602         "initial" : true,
35603         /**
35604          * @event update
35605          * Fire after update
35606          * @param {Roo.bootstrap.DocumentSlider} this
35607          */
35608         "update" : true,
35609         /**
35610          * @event click
35611          * Fire after click
35612          * @param {Roo.bootstrap.DocumentSlider} this
35613          */
35614         "click" : true
35615     });
35616 };
35617
35618 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
35619     
35620     files : false,
35621     
35622     indicator : 0,
35623     
35624     getAutoCreate : function()
35625     {
35626         var cfg = {
35627             tag : 'div',
35628             cls : 'roo-document-slider',
35629             cn : [
35630                 {
35631                     tag : 'div',
35632                     cls : 'roo-document-slider-header',
35633                     cn : [
35634                         {
35635                             tag : 'div',
35636                             cls : 'roo-document-slider-header-title'
35637                         }
35638                     ]
35639                 },
35640                 {
35641                     tag : 'div',
35642                     cls : 'roo-document-slider-body',
35643                     cn : [
35644                         {
35645                             tag : 'div',
35646                             cls : 'roo-document-slider-prev',
35647                             cn : [
35648                                 {
35649                                     tag : 'i',
35650                                     cls : 'fa fa-chevron-left'
35651                                 }
35652                             ]
35653                         },
35654                         {
35655                             tag : 'div',
35656                             cls : 'roo-document-slider-thumb',
35657                             cn : [
35658                                 {
35659                                     tag : 'img',
35660                                     cls : 'roo-document-slider-image'
35661                                 }
35662                             ]
35663                         },
35664                         {
35665                             tag : 'div',
35666                             cls : 'roo-document-slider-next',
35667                             cn : [
35668                                 {
35669                                     tag : 'i',
35670                                     cls : 'fa fa-chevron-right'
35671                                 }
35672                             ]
35673                         }
35674                     ]
35675                 }
35676             ]
35677         };
35678         
35679         return cfg;
35680     },
35681     
35682     initEvents : function()
35683     {
35684         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35685         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35686         
35687         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35688         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35689         
35690         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35691         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35692         
35693         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35694         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35695         
35696         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35697         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35698         
35699         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35700         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35701         
35702         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35703         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35704         
35705         this.thumbEl.on('click', this.onClick, this);
35706         
35707         this.prevIndicator.on('click', this.prev, this);
35708         
35709         this.nextIndicator.on('click', this.next, this);
35710         
35711     },
35712     
35713     initial : function()
35714     {
35715         if(this.files.length){
35716             this.indicator = 1;
35717             this.update()
35718         }
35719         
35720         this.fireEvent('initial', this);
35721     },
35722     
35723     update : function()
35724     {
35725         this.imageEl.attr('src', this.files[this.indicator - 1]);
35726         
35727         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35728         
35729         this.prevIndicator.show();
35730         
35731         if(this.indicator == 1){
35732             this.prevIndicator.hide();
35733         }
35734         
35735         this.nextIndicator.show();
35736         
35737         if(this.indicator == this.files.length){
35738             this.nextIndicator.hide();
35739         }
35740         
35741         this.thumbEl.scrollTo('top');
35742         
35743         this.fireEvent('update', this);
35744     },
35745     
35746     onClick : function(e)
35747     {
35748         e.preventDefault();
35749         
35750         this.fireEvent('click', this);
35751     },
35752     
35753     prev : function(e)
35754     {
35755         e.preventDefault();
35756         
35757         this.indicator = Math.max(1, this.indicator - 1);
35758         
35759         this.update();
35760     },
35761     
35762     next : function(e)
35763     {
35764         e.preventDefault();
35765         
35766         this.indicator = Math.min(this.files.length, this.indicator + 1);
35767         
35768         this.update();
35769     }
35770 });
35771 /*
35772  * - LGPL
35773  *
35774  * RadioSet
35775  *
35776  *
35777  */
35778
35779 /**
35780  * @class Roo.bootstrap.RadioSet
35781  * @extends Roo.bootstrap.Input
35782  * Bootstrap RadioSet class
35783  * @cfg {String} indicatorpos (left|right) default left
35784  * @cfg {Boolean} inline (true|false) inline the element (default true)
35785  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
35786  * @constructor
35787  * Create a new RadioSet
35788  * @param {Object} config The config object
35789  */
35790
35791 Roo.bootstrap.RadioSet = function(config){
35792     
35793     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
35794     
35795     this.radioes = [];
35796     
35797     Roo.bootstrap.RadioSet.register(this);
35798     
35799     this.addEvents({
35800         /**
35801         * @event check
35802         * Fires when the element is checked or unchecked.
35803         * @param {Roo.bootstrap.RadioSet} this This radio
35804         * @param {Roo.bootstrap.Radio} item The checked item
35805         */
35806        check : true,
35807        /**
35808         * @event click
35809         * Fires when the element is click.
35810         * @param {Roo.bootstrap.RadioSet} this This radio set
35811         * @param {Roo.bootstrap.Radio} item The checked item
35812         * @param {Roo.EventObject} e The event object
35813         */
35814        click : true
35815     });
35816     
35817 };
35818
35819 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
35820
35821     radioes : false,
35822     
35823     inline : true,
35824     
35825     weight : '',
35826     
35827     indicatorpos : 'left',
35828     
35829     getAutoCreate : function()
35830     {
35831         var label = {
35832             tag : 'label',
35833             cls : 'roo-radio-set-label',
35834             cn : [
35835                 {
35836                     tag : 'span',
35837                     html : this.fieldLabel
35838                 }
35839             ]
35840         };
35841         if (Roo.bootstrap.version == 3) {
35842             
35843             
35844             if(this.indicatorpos == 'left'){
35845                 label.cn.unshift({
35846                     tag : 'i',
35847                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
35848                     tooltip : 'This field is required'
35849                 });
35850             } else {
35851                 label.cn.push({
35852                     tag : 'i',
35853                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
35854                     tooltip : 'This field is required'
35855                 });
35856             }
35857         }
35858         var items = {
35859             tag : 'div',
35860             cls : 'roo-radio-set-items'
35861         };
35862         
35863         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
35864         
35865         if (align === 'left' && this.fieldLabel.length) {
35866             
35867             items = {
35868                 cls : "roo-radio-set-right", 
35869                 cn: [
35870                     items
35871                 ]
35872             };
35873             
35874             if(this.labelWidth > 12){
35875                 label.style = "width: " + this.labelWidth + 'px';
35876             }
35877             
35878             if(this.labelWidth < 13 && this.labelmd == 0){
35879                 this.labelmd = this.labelWidth;
35880             }
35881             
35882             if(this.labellg > 0){
35883                 label.cls += ' col-lg-' + this.labellg;
35884                 items.cls += ' col-lg-' + (12 - this.labellg);
35885             }
35886             
35887             if(this.labelmd > 0){
35888                 label.cls += ' col-md-' + this.labelmd;
35889                 items.cls += ' col-md-' + (12 - this.labelmd);
35890             }
35891             
35892             if(this.labelsm > 0){
35893                 label.cls += ' col-sm-' + this.labelsm;
35894                 items.cls += ' col-sm-' + (12 - this.labelsm);
35895             }
35896             
35897             if(this.labelxs > 0){
35898                 label.cls += ' col-xs-' + this.labelxs;
35899                 items.cls += ' col-xs-' + (12 - this.labelxs);
35900             }
35901         }
35902         
35903         var cfg = {
35904             tag : 'div',
35905             cls : 'roo-radio-set',
35906             cn : [
35907                 {
35908                     tag : 'input',
35909                     cls : 'roo-radio-set-input',
35910                     type : 'hidden',
35911                     name : this.name,
35912                     value : this.value ? this.value :  ''
35913                 },
35914                 label,
35915                 items
35916             ]
35917         };
35918         
35919         if(this.weight.length){
35920             cfg.cls += ' roo-radio-' + this.weight;
35921         }
35922         
35923         if(this.inline) {
35924             cfg.cls += ' roo-radio-set-inline';
35925         }
35926         
35927         var settings=this;
35928         ['xs','sm','md','lg'].map(function(size){
35929             if (settings[size]) {
35930                 cfg.cls += ' col-' + size + '-' + settings[size];
35931             }
35932         });
35933         
35934         return cfg;
35935         
35936     },
35937
35938     initEvents : function()
35939     {
35940         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
35941         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
35942         
35943         if(!this.fieldLabel.length){
35944             this.labelEl.hide();
35945         }
35946         
35947         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
35948         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
35949         
35950         this.indicator = this.indicatorEl();
35951         
35952         if(this.indicator){
35953             this.indicator.addClass('invisible');
35954         }
35955         
35956         this.originalValue = this.getValue();
35957         
35958     },
35959     
35960     inputEl: function ()
35961     {
35962         return this.el.select('.roo-radio-set-input', true).first();
35963     },
35964     
35965     getChildContainer : function()
35966     {
35967         return this.itemsEl;
35968     },
35969     
35970     register : function(item)
35971     {
35972         this.radioes.push(item);
35973         
35974     },
35975     
35976     validate : function()
35977     {   
35978         if(this.getVisibilityEl().hasClass('hidden')){
35979             return true;
35980         }
35981         
35982         var valid = false;
35983         
35984         Roo.each(this.radioes, function(i){
35985             if(!i.checked){
35986                 return;
35987             }
35988             
35989             valid = true;
35990             return false;
35991         });
35992         
35993         if(this.allowBlank) {
35994             return true;
35995         }
35996         
35997         if(this.disabled || valid){
35998             this.markValid();
35999             return true;
36000         }
36001         
36002         this.markInvalid();
36003         return false;
36004         
36005     },
36006     
36007     markValid : function()
36008     {
36009         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36010             this.indicatorEl().removeClass('visible');
36011             this.indicatorEl().addClass('invisible');
36012         }
36013         
36014         
36015         if (Roo.bootstrap.version == 3) {
36016             this.el.removeClass([this.invalidClass, this.validClass]);
36017             this.el.addClass(this.validClass);
36018         } else {
36019             this.el.removeClass(['is-invalid','is-valid']);
36020             this.el.addClass(['is-valid']);
36021         }
36022         this.fireEvent('valid', this);
36023     },
36024     
36025     markInvalid : function(msg)
36026     {
36027         if(this.allowBlank || this.disabled){
36028             return;
36029         }
36030         
36031         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36032             this.indicatorEl().removeClass('invisible');
36033             this.indicatorEl().addClass('visible');
36034         }
36035         if (Roo.bootstrap.version == 3) {
36036             this.el.removeClass([this.invalidClass, this.validClass]);
36037             this.el.addClass(this.invalidClass);
36038         } else {
36039             this.el.removeClass(['is-invalid','is-valid']);
36040             this.el.addClass(['is-invalid']);
36041         }
36042         
36043         this.fireEvent('invalid', this, msg);
36044         
36045     },
36046     
36047     setValue : function(v, suppressEvent)
36048     {   
36049         if(this.value === v){
36050             return;
36051         }
36052         
36053         this.value = v;
36054         
36055         if(this.rendered){
36056             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36057         }
36058         
36059         Roo.each(this.radioes, function(i){
36060             i.checked = false;
36061             i.el.removeClass('checked');
36062         });
36063         
36064         Roo.each(this.radioes, function(i){
36065             
36066             if(i.value === v || i.value.toString() === v.toString()){
36067                 i.checked = true;
36068                 i.el.addClass('checked');
36069                 
36070                 if(suppressEvent !== true){
36071                     this.fireEvent('check', this, i);
36072                 }
36073                 
36074                 return false;
36075             }
36076             
36077         }, this);
36078         
36079         this.validate();
36080     },
36081     
36082     clearInvalid : function(){
36083         
36084         if(!this.el || this.preventMark){
36085             return;
36086         }
36087         
36088         this.el.removeClass([this.invalidClass]);
36089         
36090         this.fireEvent('valid', this);
36091     }
36092     
36093 });
36094
36095 Roo.apply(Roo.bootstrap.RadioSet, {
36096     
36097     groups: {},
36098     
36099     register : function(set)
36100     {
36101         this.groups[set.name] = set;
36102     },
36103     
36104     get: function(name) 
36105     {
36106         if (typeof(this.groups[name]) == 'undefined') {
36107             return false;
36108         }
36109         
36110         return this.groups[name] ;
36111     }
36112     
36113 });
36114 /*
36115  * Based on:
36116  * Ext JS Library 1.1.1
36117  * Copyright(c) 2006-2007, Ext JS, LLC.
36118  *
36119  * Originally Released Under LGPL - original licence link has changed is not relivant.
36120  *
36121  * Fork - LGPL
36122  * <script type="text/javascript">
36123  */
36124
36125
36126 /**
36127  * @class Roo.bootstrap.SplitBar
36128  * @extends Roo.util.Observable
36129  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36130  * <br><br>
36131  * Usage:
36132  * <pre><code>
36133 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36134                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36135 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36136 split.minSize = 100;
36137 split.maxSize = 600;
36138 split.animate = true;
36139 split.on('moved', splitterMoved);
36140 </code></pre>
36141  * @constructor
36142  * Create a new SplitBar
36143  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36144  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36145  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36146  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36147                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36148                         position of the SplitBar).
36149  */
36150 Roo.bootstrap.SplitBar = function(cfg){
36151     
36152     /** @private */
36153     
36154     //{
36155     //  dragElement : elm
36156     //  resizingElement: el,
36157         // optional..
36158     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36159     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36160         // existingProxy ???
36161     //}
36162     
36163     this.el = Roo.get(cfg.dragElement, true);
36164     this.el.dom.unselectable = "on";
36165     /** @private */
36166     this.resizingEl = Roo.get(cfg.resizingElement, true);
36167
36168     /**
36169      * @private
36170      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36171      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36172      * @type Number
36173      */
36174     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36175     
36176     /**
36177      * The minimum size of the resizing element. (Defaults to 0)
36178      * @type Number
36179      */
36180     this.minSize = 0;
36181     
36182     /**
36183      * The maximum size of the resizing element. (Defaults to 2000)
36184      * @type Number
36185      */
36186     this.maxSize = 2000;
36187     
36188     /**
36189      * Whether to animate the transition to the new size
36190      * @type Boolean
36191      */
36192     this.animate = false;
36193     
36194     /**
36195      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36196      * @type Boolean
36197      */
36198     this.useShim = false;
36199     
36200     /** @private */
36201     this.shim = null;
36202     
36203     if(!cfg.existingProxy){
36204         /** @private */
36205         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36206     }else{
36207         this.proxy = Roo.get(cfg.existingProxy).dom;
36208     }
36209     /** @private */
36210     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36211     
36212     /** @private */
36213     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36214     
36215     /** @private */
36216     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36217     
36218     /** @private */
36219     this.dragSpecs = {};
36220     
36221     /**
36222      * @private The adapter to use to positon and resize elements
36223      */
36224     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36225     this.adapter.init(this);
36226     
36227     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36228         /** @private */
36229         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36230         this.el.addClass("roo-splitbar-h");
36231     }else{
36232         /** @private */
36233         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36234         this.el.addClass("roo-splitbar-v");
36235     }
36236     
36237     this.addEvents({
36238         /**
36239          * @event resize
36240          * Fires when the splitter is moved (alias for {@link #event-moved})
36241          * @param {Roo.bootstrap.SplitBar} this
36242          * @param {Number} newSize the new width or height
36243          */
36244         "resize" : true,
36245         /**
36246          * @event moved
36247          * Fires when the splitter is moved
36248          * @param {Roo.bootstrap.SplitBar} this
36249          * @param {Number} newSize the new width or height
36250          */
36251         "moved" : true,
36252         /**
36253          * @event beforeresize
36254          * Fires before the splitter is dragged
36255          * @param {Roo.bootstrap.SplitBar} this
36256          */
36257         "beforeresize" : true,
36258
36259         "beforeapply" : true
36260     });
36261
36262     Roo.util.Observable.call(this);
36263 };
36264
36265 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36266     onStartProxyDrag : function(x, y){
36267         this.fireEvent("beforeresize", this);
36268         if(!this.overlay){
36269             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36270             o.unselectable();
36271             o.enableDisplayMode("block");
36272             // all splitbars share the same overlay
36273             Roo.bootstrap.SplitBar.prototype.overlay = o;
36274         }
36275         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36276         this.overlay.show();
36277         Roo.get(this.proxy).setDisplayed("block");
36278         var size = this.adapter.getElementSize(this);
36279         this.activeMinSize = this.getMinimumSize();;
36280         this.activeMaxSize = this.getMaximumSize();;
36281         var c1 = size - this.activeMinSize;
36282         var c2 = Math.max(this.activeMaxSize - size, 0);
36283         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36284             this.dd.resetConstraints();
36285             this.dd.setXConstraint(
36286                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36287                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36288             );
36289             this.dd.setYConstraint(0, 0);
36290         }else{
36291             this.dd.resetConstraints();
36292             this.dd.setXConstraint(0, 0);
36293             this.dd.setYConstraint(
36294                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36295                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36296             );
36297          }
36298         this.dragSpecs.startSize = size;
36299         this.dragSpecs.startPoint = [x, y];
36300         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36301     },
36302     
36303     /** 
36304      * @private Called after the drag operation by the DDProxy
36305      */
36306     onEndProxyDrag : function(e){
36307         Roo.get(this.proxy).setDisplayed(false);
36308         var endPoint = Roo.lib.Event.getXY(e);
36309         if(this.overlay){
36310             this.overlay.hide();
36311         }
36312         var newSize;
36313         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36314             newSize = this.dragSpecs.startSize + 
36315                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36316                     endPoint[0] - this.dragSpecs.startPoint[0] :
36317                     this.dragSpecs.startPoint[0] - endPoint[0]
36318                 );
36319         }else{
36320             newSize = this.dragSpecs.startSize + 
36321                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36322                     endPoint[1] - this.dragSpecs.startPoint[1] :
36323                     this.dragSpecs.startPoint[1] - endPoint[1]
36324                 );
36325         }
36326         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36327         if(newSize != this.dragSpecs.startSize){
36328             if(this.fireEvent('beforeapply', this, newSize) !== false){
36329                 this.adapter.setElementSize(this, newSize);
36330                 this.fireEvent("moved", this, newSize);
36331                 this.fireEvent("resize", this, newSize);
36332             }
36333         }
36334     },
36335     
36336     /**
36337      * Get the adapter this SplitBar uses
36338      * @return The adapter object
36339      */
36340     getAdapter : function(){
36341         return this.adapter;
36342     },
36343     
36344     /**
36345      * Set the adapter this SplitBar uses
36346      * @param {Object} adapter A SplitBar adapter object
36347      */
36348     setAdapter : function(adapter){
36349         this.adapter = adapter;
36350         this.adapter.init(this);
36351     },
36352     
36353     /**
36354      * Gets the minimum size for the resizing element
36355      * @return {Number} The minimum size
36356      */
36357     getMinimumSize : function(){
36358         return this.minSize;
36359     },
36360     
36361     /**
36362      * Sets the minimum size for the resizing element
36363      * @param {Number} minSize The minimum size
36364      */
36365     setMinimumSize : function(minSize){
36366         this.minSize = minSize;
36367     },
36368     
36369     /**
36370      * Gets the maximum size for the resizing element
36371      * @return {Number} The maximum size
36372      */
36373     getMaximumSize : function(){
36374         return this.maxSize;
36375     },
36376     
36377     /**
36378      * Sets the maximum size for the resizing element
36379      * @param {Number} maxSize The maximum size
36380      */
36381     setMaximumSize : function(maxSize){
36382         this.maxSize = maxSize;
36383     },
36384     
36385     /**
36386      * Sets the initialize size for the resizing element
36387      * @param {Number} size The initial size
36388      */
36389     setCurrentSize : function(size){
36390         var oldAnimate = this.animate;
36391         this.animate = false;
36392         this.adapter.setElementSize(this, size);
36393         this.animate = oldAnimate;
36394     },
36395     
36396     /**
36397      * Destroy this splitbar. 
36398      * @param {Boolean} removeEl True to remove the element
36399      */
36400     destroy : function(removeEl){
36401         if(this.shim){
36402             this.shim.remove();
36403         }
36404         this.dd.unreg();
36405         this.proxy.parentNode.removeChild(this.proxy);
36406         if(removeEl){
36407             this.el.remove();
36408         }
36409     }
36410 });
36411
36412 /**
36413  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
36414  */
36415 Roo.bootstrap.SplitBar.createProxy = function(dir){
36416     var proxy = new Roo.Element(document.createElement("div"));
36417     proxy.unselectable();
36418     var cls = 'roo-splitbar-proxy';
36419     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36420     document.body.appendChild(proxy.dom);
36421     return proxy.dom;
36422 };
36423
36424 /** 
36425  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36426  * Default Adapter. It assumes the splitter and resizing element are not positioned
36427  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36428  */
36429 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36430 };
36431
36432 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36433     // do nothing for now
36434     init : function(s){
36435     
36436     },
36437     /**
36438      * Called before drag operations to get the current size of the resizing element. 
36439      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36440      */
36441      getElementSize : function(s){
36442         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36443             return s.resizingEl.getWidth();
36444         }else{
36445             return s.resizingEl.getHeight();
36446         }
36447     },
36448     
36449     /**
36450      * Called after drag operations to set the size of the resizing element.
36451      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36452      * @param {Number} newSize The new size to set
36453      * @param {Function} onComplete A function to be invoked when resizing is complete
36454      */
36455     setElementSize : function(s, newSize, onComplete){
36456         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36457             if(!s.animate){
36458                 s.resizingEl.setWidth(newSize);
36459                 if(onComplete){
36460                     onComplete(s, newSize);
36461                 }
36462             }else{
36463                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36464             }
36465         }else{
36466             
36467             if(!s.animate){
36468                 s.resizingEl.setHeight(newSize);
36469                 if(onComplete){
36470                     onComplete(s, newSize);
36471                 }
36472             }else{
36473                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36474             }
36475         }
36476     }
36477 };
36478
36479 /** 
36480  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36481  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36482  * Adapter that  moves the splitter element to align with the resized sizing element. 
36483  * Used with an absolute positioned SplitBar.
36484  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36485  * document.body, make sure you assign an id to the body element.
36486  */
36487 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36488     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36489     this.container = Roo.get(container);
36490 };
36491
36492 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36493     init : function(s){
36494         this.basic.init(s);
36495     },
36496     
36497     getElementSize : function(s){
36498         return this.basic.getElementSize(s);
36499     },
36500     
36501     setElementSize : function(s, newSize, onComplete){
36502         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36503     },
36504     
36505     moveSplitter : function(s){
36506         var yes = Roo.bootstrap.SplitBar;
36507         switch(s.placement){
36508             case yes.LEFT:
36509                 s.el.setX(s.resizingEl.getRight());
36510                 break;
36511             case yes.RIGHT:
36512                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36513                 break;
36514             case yes.TOP:
36515                 s.el.setY(s.resizingEl.getBottom());
36516                 break;
36517             case yes.BOTTOM:
36518                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36519                 break;
36520         }
36521     }
36522 };
36523
36524 /**
36525  * Orientation constant - Create a vertical SplitBar
36526  * @static
36527  * @type Number
36528  */
36529 Roo.bootstrap.SplitBar.VERTICAL = 1;
36530
36531 /**
36532  * Orientation constant - Create a horizontal SplitBar
36533  * @static
36534  * @type Number
36535  */
36536 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36537
36538 /**
36539  * Placement constant - The resizing element is to the left of the splitter element
36540  * @static
36541  * @type Number
36542  */
36543 Roo.bootstrap.SplitBar.LEFT = 1;
36544
36545 /**
36546  * Placement constant - The resizing element is to the right of the splitter element
36547  * @static
36548  * @type Number
36549  */
36550 Roo.bootstrap.SplitBar.RIGHT = 2;
36551
36552 /**
36553  * Placement constant - The resizing element is positioned above the splitter element
36554  * @static
36555  * @type Number
36556  */
36557 Roo.bootstrap.SplitBar.TOP = 3;
36558
36559 /**
36560  * Placement constant - The resizing element is positioned under splitter element
36561  * @static
36562  * @type Number
36563  */
36564 Roo.bootstrap.SplitBar.BOTTOM = 4;
36565 Roo.namespace("Roo.bootstrap.layout");/*
36566  * Based on:
36567  * Ext JS Library 1.1.1
36568  * Copyright(c) 2006-2007, Ext JS, LLC.
36569  *
36570  * Originally Released Under LGPL - original licence link has changed is not relivant.
36571  *
36572  * Fork - LGPL
36573  * <script type="text/javascript">
36574  */
36575
36576 /**
36577  * @class Roo.bootstrap.layout.Manager
36578  * @extends Roo.bootstrap.Component
36579  * Base class for layout managers.
36580  */
36581 Roo.bootstrap.layout.Manager = function(config)
36582 {
36583     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36584
36585
36586
36587
36588
36589     /** false to disable window resize monitoring @type Boolean */
36590     this.monitorWindowResize = true;
36591     this.regions = {};
36592     this.addEvents({
36593         /**
36594          * @event layout
36595          * Fires when a layout is performed.
36596          * @param {Roo.LayoutManager} this
36597          */
36598         "layout" : true,
36599         /**
36600          * @event regionresized
36601          * Fires when the user resizes a region.
36602          * @param {Roo.LayoutRegion} region The resized region
36603          * @param {Number} newSize The new size (width for east/west, height for north/south)
36604          */
36605         "regionresized" : true,
36606         /**
36607          * @event regioncollapsed
36608          * Fires when a region is collapsed.
36609          * @param {Roo.LayoutRegion} region The collapsed region
36610          */
36611         "regioncollapsed" : true,
36612         /**
36613          * @event regionexpanded
36614          * Fires when a region is expanded.
36615          * @param {Roo.LayoutRegion} region The expanded region
36616          */
36617         "regionexpanded" : true
36618     });
36619     this.updating = false;
36620
36621     if (config.el) {
36622         this.el = Roo.get(config.el);
36623         this.initEvents();
36624     }
36625
36626 };
36627
36628 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36629
36630
36631     regions : null,
36632
36633     monitorWindowResize : true,
36634
36635
36636     updating : false,
36637
36638
36639     onRender : function(ct, position)
36640     {
36641         if(!this.el){
36642             this.el = Roo.get(ct);
36643             this.initEvents();
36644         }
36645         //this.fireEvent('render',this);
36646     },
36647
36648
36649     initEvents: function()
36650     {
36651
36652
36653         // ie scrollbar fix
36654         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36655             document.body.scroll = "no";
36656         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36657             this.el.position('relative');
36658         }
36659         this.id = this.el.id;
36660         this.el.addClass("roo-layout-container");
36661         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36662         if(this.el.dom != document.body ) {
36663             this.el.on('resize', this.layout,this);
36664             this.el.on('show', this.layout,this);
36665         }
36666
36667     },
36668
36669     /**
36670      * Returns true if this layout is currently being updated
36671      * @return {Boolean}
36672      */
36673     isUpdating : function(){
36674         return this.updating;
36675     },
36676
36677     /**
36678      * Suspend the LayoutManager from doing auto-layouts while
36679      * making multiple add or remove calls
36680      */
36681     beginUpdate : function(){
36682         this.updating = true;
36683     },
36684
36685     /**
36686      * Restore auto-layouts and optionally disable the manager from performing a layout
36687      * @param {Boolean} noLayout true to disable a layout update
36688      */
36689     endUpdate : function(noLayout){
36690         this.updating = false;
36691         if(!noLayout){
36692             this.layout();
36693         }
36694     },
36695
36696     layout: function(){
36697         // abstract...
36698     },
36699
36700     onRegionResized : function(region, newSize){
36701         this.fireEvent("regionresized", region, newSize);
36702         this.layout();
36703     },
36704
36705     onRegionCollapsed : function(region){
36706         this.fireEvent("regioncollapsed", region);
36707     },
36708
36709     onRegionExpanded : function(region){
36710         this.fireEvent("regionexpanded", region);
36711     },
36712
36713     /**
36714      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36715      * performs box-model adjustments.
36716      * @return {Object} The size as an object {width: (the width), height: (the height)}
36717      */
36718     getViewSize : function()
36719     {
36720         var size;
36721         if(this.el.dom != document.body){
36722             size = this.el.getSize();
36723         }else{
36724             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36725         }
36726         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36727         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36728         return size;
36729     },
36730
36731     /**
36732      * Returns the Element this layout is bound to.
36733      * @return {Roo.Element}
36734      */
36735     getEl : function(){
36736         return this.el;
36737     },
36738
36739     /**
36740      * Returns the specified region.
36741      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36742      * @return {Roo.LayoutRegion}
36743      */
36744     getRegion : function(target){
36745         return this.regions[target.toLowerCase()];
36746     },
36747
36748     onWindowResize : function(){
36749         if(this.monitorWindowResize){
36750             this.layout();
36751         }
36752     }
36753 });
36754 /*
36755  * Based on:
36756  * Ext JS Library 1.1.1
36757  * Copyright(c) 2006-2007, Ext JS, LLC.
36758  *
36759  * Originally Released Under LGPL - original licence link has changed is not relivant.
36760  *
36761  * Fork - LGPL
36762  * <script type="text/javascript">
36763  */
36764 /**
36765  * @class Roo.bootstrap.layout.Border
36766  * @extends Roo.bootstrap.layout.Manager
36767  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36768  * please see: examples/bootstrap/nested.html<br><br>
36769  
36770 <b>The container the layout is rendered into can be either the body element or any other element.
36771 If it is not the body element, the container needs to either be an absolute positioned element,
36772 or you will need to add "position:relative" to the css of the container.  You will also need to specify
36773 the container size if it is not the body element.</b>
36774
36775 * @constructor
36776 * Create a new Border
36777 * @param {Object} config Configuration options
36778  */
36779 Roo.bootstrap.layout.Border = function(config){
36780     config = config || {};
36781     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
36782     
36783     
36784     
36785     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36786         if(config[region]){
36787             config[region].region = region;
36788             this.addRegion(config[region]);
36789         }
36790     },this);
36791     
36792 };
36793
36794 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
36795
36796 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
36797     
36798     parent : false, // this might point to a 'nest' or a ???
36799     
36800     /**
36801      * Creates and adds a new region if it doesn't already exist.
36802      * @param {String} target The target region key (north, south, east, west or center).
36803      * @param {Object} config The regions config object
36804      * @return {BorderLayoutRegion} The new region
36805      */
36806     addRegion : function(config)
36807     {
36808         if(!this.regions[config.region]){
36809             var r = this.factory(config);
36810             this.bindRegion(r);
36811         }
36812         return this.regions[config.region];
36813     },
36814
36815     // private (kinda)
36816     bindRegion : function(r){
36817         this.regions[r.config.region] = r;
36818         
36819         r.on("visibilitychange",    this.layout, this);
36820         r.on("paneladded",          this.layout, this);
36821         r.on("panelremoved",        this.layout, this);
36822         r.on("invalidated",         this.layout, this);
36823         r.on("resized",             this.onRegionResized, this);
36824         r.on("collapsed",           this.onRegionCollapsed, this);
36825         r.on("expanded",            this.onRegionExpanded, this);
36826     },
36827
36828     /**
36829      * Performs a layout update.
36830      */
36831     layout : function()
36832     {
36833         if(this.updating) {
36834             return;
36835         }
36836         
36837         // render all the rebions if they have not been done alreayd?
36838         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36839             if(this.regions[region] && !this.regions[region].bodyEl){
36840                 this.regions[region].onRender(this.el)
36841             }
36842         },this);
36843         
36844         var size = this.getViewSize();
36845         var w = size.width;
36846         var h = size.height;
36847         var centerW = w;
36848         var centerH = h;
36849         var centerY = 0;
36850         var centerX = 0;
36851         //var x = 0, y = 0;
36852
36853         var rs = this.regions;
36854         var north = rs["north"];
36855         var south = rs["south"]; 
36856         var west = rs["west"];
36857         var east = rs["east"];
36858         var center = rs["center"];
36859         //if(this.hideOnLayout){ // not supported anymore
36860             //c.el.setStyle("display", "none");
36861         //}
36862         if(north && north.isVisible()){
36863             var b = north.getBox();
36864             var m = north.getMargins();
36865             b.width = w - (m.left+m.right);
36866             b.x = m.left;
36867             b.y = m.top;
36868             centerY = b.height + b.y + m.bottom;
36869             centerH -= centerY;
36870             north.updateBox(this.safeBox(b));
36871         }
36872         if(south && south.isVisible()){
36873             var b = south.getBox();
36874             var m = south.getMargins();
36875             b.width = w - (m.left+m.right);
36876             b.x = m.left;
36877             var totalHeight = (b.height + m.top + m.bottom);
36878             b.y = h - totalHeight + m.top;
36879             centerH -= totalHeight;
36880             south.updateBox(this.safeBox(b));
36881         }
36882         if(west && west.isVisible()){
36883             var b = west.getBox();
36884             var m = west.getMargins();
36885             b.height = centerH - (m.top+m.bottom);
36886             b.x = m.left;
36887             b.y = centerY + m.top;
36888             var totalWidth = (b.width + m.left + m.right);
36889             centerX += totalWidth;
36890             centerW -= totalWidth;
36891             west.updateBox(this.safeBox(b));
36892         }
36893         if(east && east.isVisible()){
36894             var b = east.getBox();
36895             var m = east.getMargins();
36896             b.height = centerH - (m.top+m.bottom);
36897             var totalWidth = (b.width + m.left + m.right);
36898             b.x = w - totalWidth + m.left;
36899             b.y = centerY + m.top;
36900             centerW -= totalWidth;
36901             east.updateBox(this.safeBox(b));
36902         }
36903         if(center){
36904             var m = center.getMargins();
36905             var centerBox = {
36906                 x: centerX + m.left,
36907                 y: centerY + m.top,
36908                 width: centerW - (m.left+m.right),
36909                 height: centerH - (m.top+m.bottom)
36910             };
36911             //if(this.hideOnLayout){
36912                 //center.el.setStyle("display", "block");
36913             //}
36914             center.updateBox(this.safeBox(centerBox));
36915         }
36916         this.el.repaint();
36917         this.fireEvent("layout", this);
36918     },
36919
36920     // private
36921     safeBox : function(box){
36922         box.width = Math.max(0, box.width);
36923         box.height = Math.max(0, box.height);
36924         return box;
36925     },
36926
36927     /**
36928      * Adds a ContentPanel (or subclass) to this layout.
36929      * @param {String} target The target region key (north, south, east, west or center).
36930      * @param {Roo.ContentPanel} panel The panel to add
36931      * @return {Roo.ContentPanel} The added panel
36932      */
36933     add : function(target, panel){
36934          
36935         target = target.toLowerCase();
36936         return this.regions[target].add(panel);
36937     },
36938
36939     /**
36940      * Remove a ContentPanel (or subclass) to this layout.
36941      * @param {String} target The target region key (north, south, east, west or center).
36942      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
36943      * @return {Roo.ContentPanel} The removed panel
36944      */
36945     remove : function(target, panel){
36946         target = target.toLowerCase();
36947         return this.regions[target].remove(panel);
36948     },
36949
36950     /**
36951      * Searches all regions for a panel with the specified id
36952      * @param {String} panelId
36953      * @return {Roo.ContentPanel} The panel or null if it wasn't found
36954      */
36955     findPanel : function(panelId){
36956         var rs = this.regions;
36957         for(var target in rs){
36958             if(typeof rs[target] != "function"){
36959                 var p = rs[target].getPanel(panelId);
36960                 if(p){
36961                     return p;
36962                 }
36963             }
36964         }
36965         return null;
36966     },
36967
36968     /**
36969      * Searches all regions for a panel with the specified id and activates (shows) it.
36970      * @param {String/ContentPanel} panelId The panels id or the panel itself
36971      * @return {Roo.ContentPanel} The shown panel or null
36972      */
36973     showPanel : function(panelId) {
36974       var rs = this.regions;
36975       for(var target in rs){
36976          var r = rs[target];
36977          if(typeof r != "function"){
36978             if(r.hasPanel(panelId)){
36979                return r.showPanel(panelId);
36980             }
36981          }
36982       }
36983       return null;
36984    },
36985
36986    /**
36987      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
36988      * @param {Roo.state.Provider} provider (optional) An alternate state provider
36989      */
36990    /*
36991     restoreState : function(provider){
36992         if(!provider){
36993             provider = Roo.state.Manager;
36994         }
36995         var sm = new Roo.LayoutStateManager();
36996         sm.init(this, provider);
36997     },
36998 */
36999  
37000  
37001     /**
37002      * Adds a xtype elements to the layout.
37003      * <pre><code>
37004
37005 layout.addxtype({
37006        xtype : 'ContentPanel',
37007        region: 'west',
37008        items: [ .... ]
37009    }
37010 );
37011
37012 layout.addxtype({
37013         xtype : 'NestedLayoutPanel',
37014         region: 'west',
37015         layout: {
37016            center: { },
37017            west: { }   
37018         },
37019         items : [ ... list of content panels or nested layout panels.. ]
37020    }
37021 );
37022 </code></pre>
37023      * @param {Object} cfg Xtype definition of item to add.
37024      */
37025     addxtype : function(cfg)
37026     {
37027         // basically accepts a pannel...
37028         // can accept a layout region..!?!?
37029         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37030         
37031         
37032         // theory?  children can only be panels??
37033         
37034         //if (!cfg.xtype.match(/Panel$/)) {
37035         //    return false;
37036         //}
37037         var ret = false;
37038         
37039         if (typeof(cfg.region) == 'undefined') {
37040             Roo.log("Failed to add Panel, region was not set");
37041             Roo.log(cfg);
37042             return false;
37043         }
37044         var region = cfg.region;
37045         delete cfg.region;
37046         
37047           
37048         var xitems = [];
37049         if (cfg.items) {
37050             xitems = cfg.items;
37051             delete cfg.items;
37052         }
37053         var nb = false;
37054         
37055         if ( region == 'center') {
37056             Roo.log("Center: " + cfg.title);
37057         }
37058         
37059         
37060         switch(cfg.xtype) 
37061         {
37062             case 'Content':  // ContentPanel (el, cfg)
37063             case 'Scroll':  // ContentPanel (el, cfg)
37064             case 'View': 
37065                 cfg.autoCreate = cfg.autoCreate || true;
37066                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37067                 //} else {
37068                 //    var el = this.el.createChild();
37069                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37070                 //}
37071                 
37072                 this.add(region, ret);
37073                 break;
37074             
37075             /*
37076             case 'TreePanel': // our new panel!
37077                 cfg.el = this.el.createChild();
37078                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37079                 this.add(region, ret);
37080                 break;
37081             */
37082             
37083             case 'Nest': 
37084                 // create a new Layout (which is  a Border Layout...
37085                 
37086                 var clayout = cfg.layout;
37087                 clayout.el  = this.el.createChild();
37088                 clayout.items   = clayout.items  || [];
37089                 
37090                 delete cfg.layout;
37091                 
37092                 // replace this exitems with the clayout ones..
37093                 xitems = clayout.items;
37094                  
37095                 // force background off if it's in center...
37096                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37097                     cfg.background = false;
37098                 }
37099                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37100                 
37101                 
37102                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37103                 //console.log('adding nested layout panel '  + cfg.toSource());
37104                 this.add(region, ret);
37105                 nb = {}; /// find first...
37106                 break;
37107             
37108             case 'Grid':
37109                 
37110                 // needs grid and region
37111                 
37112                 //var el = this.getRegion(region).el.createChild();
37113                 /*
37114                  *var el = this.el.createChild();
37115                 // create the grid first...
37116                 cfg.grid.container = el;
37117                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37118                 */
37119                 
37120                 if (region == 'center' && this.active ) {
37121                     cfg.background = false;
37122                 }
37123                 
37124                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37125                 
37126                 this.add(region, ret);
37127                 /*
37128                 if (cfg.background) {
37129                     // render grid on panel activation (if panel background)
37130                     ret.on('activate', function(gp) {
37131                         if (!gp.grid.rendered) {
37132                     //        gp.grid.render(el);
37133                         }
37134                     });
37135                 } else {
37136                   //  cfg.grid.render(el);
37137                 }
37138                 */
37139                 break;
37140            
37141            
37142             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37143                 // it was the old xcomponent building that caused this before.
37144                 // espeically if border is the top element in the tree.
37145                 ret = this;
37146                 break; 
37147                 
37148                     
37149                 
37150                 
37151                 
37152             default:
37153                 /*
37154                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37155                     
37156                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37157                     this.add(region, ret);
37158                 } else {
37159                 */
37160                     Roo.log(cfg);
37161                     throw "Can not add '" + cfg.xtype + "' to Border";
37162                     return null;
37163              
37164                                 
37165              
37166         }
37167         this.beginUpdate();
37168         // add children..
37169         var region = '';
37170         var abn = {};
37171         Roo.each(xitems, function(i)  {
37172             region = nb && i.region ? i.region : false;
37173             
37174             var add = ret.addxtype(i);
37175            
37176             if (region) {
37177                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37178                 if (!i.background) {
37179                     abn[region] = nb[region] ;
37180                 }
37181             }
37182             
37183         });
37184         this.endUpdate();
37185
37186         // make the last non-background panel active..
37187         //if (nb) { Roo.log(abn); }
37188         if (nb) {
37189             
37190             for(var r in abn) {
37191                 region = this.getRegion(r);
37192                 if (region) {
37193                     // tried using nb[r], but it does not work..
37194                      
37195                     region.showPanel(abn[r]);
37196                    
37197                 }
37198             }
37199         }
37200         return ret;
37201         
37202     },
37203     
37204     
37205 // private
37206     factory : function(cfg)
37207     {
37208         
37209         var validRegions = Roo.bootstrap.layout.Border.regions;
37210
37211         var target = cfg.region;
37212         cfg.mgr = this;
37213         
37214         var r = Roo.bootstrap.layout;
37215         Roo.log(target);
37216         switch(target){
37217             case "north":
37218                 return new r.North(cfg);
37219             case "south":
37220                 return new r.South(cfg);
37221             case "east":
37222                 return new r.East(cfg);
37223             case "west":
37224                 return new r.West(cfg);
37225             case "center":
37226                 return new r.Center(cfg);
37227         }
37228         throw 'Layout region "'+target+'" not supported.';
37229     }
37230     
37231     
37232 });
37233  /*
37234  * Based on:
37235  * Ext JS Library 1.1.1
37236  * Copyright(c) 2006-2007, Ext JS, LLC.
37237  *
37238  * Originally Released Under LGPL - original licence link has changed is not relivant.
37239  *
37240  * Fork - LGPL
37241  * <script type="text/javascript">
37242  */
37243  
37244 /**
37245  * @class Roo.bootstrap.layout.Basic
37246  * @extends Roo.util.Observable
37247  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37248  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37249  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37250  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37251  * @cfg {string}   region  the region that it inhabits..
37252  * @cfg {bool}   skipConfig skip config?
37253  * 
37254
37255  */
37256 Roo.bootstrap.layout.Basic = function(config){
37257     
37258     this.mgr = config.mgr;
37259     
37260     this.position = config.region;
37261     
37262     var skipConfig = config.skipConfig;
37263     
37264     this.events = {
37265         /**
37266          * @scope Roo.BasicLayoutRegion
37267          */
37268         
37269         /**
37270          * @event beforeremove
37271          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37272          * @param {Roo.LayoutRegion} this
37273          * @param {Roo.ContentPanel} panel The panel
37274          * @param {Object} e The cancel event object
37275          */
37276         "beforeremove" : true,
37277         /**
37278          * @event invalidated
37279          * Fires when the layout for this region is changed.
37280          * @param {Roo.LayoutRegion} this
37281          */
37282         "invalidated" : true,
37283         /**
37284          * @event visibilitychange
37285          * Fires when this region is shown or hidden 
37286          * @param {Roo.LayoutRegion} this
37287          * @param {Boolean} visibility true or false
37288          */
37289         "visibilitychange" : true,
37290         /**
37291          * @event paneladded
37292          * Fires when a panel is added. 
37293          * @param {Roo.LayoutRegion} this
37294          * @param {Roo.ContentPanel} panel The panel
37295          */
37296         "paneladded" : true,
37297         /**
37298          * @event panelremoved
37299          * Fires when a panel is removed. 
37300          * @param {Roo.LayoutRegion} this
37301          * @param {Roo.ContentPanel} panel The panel
37302          */
37303         "panelremoved" : true,
37304         /**
37305          * @event beforecollapse
37306          * Fires when this region before collapse.
37307          * @param {Roo.LayoutRegion} this
37308          */
37309         "beforecollapse" : true,
37310         /**
37311          * @event collapsed
37312          * Fires when this region is collapsed.
37313          * @param {Roo.LayoutRegion} this
37314          */
37315         "collapsed" : true,
37316         /**
37317          * @event expanded
37318          * Fires when this region is expanded.
37319          * @param {Roo.LayoutRegion} this
37320          */
37321         "expanded" : true,
37322         /**
37323          * @event slideshow
37324          * Fires when this region is slid into view.
37325          * @param {Roo.LayoutRegion} this
37326          */
37327         "slideshow" : true,
37328         /**
37329          * @event slidehide
37330          * Fires when this region slides out of view. 
37331          * @param {Roo.LayoutRegion} this
37332          */
37333         "slidehide" : true,
37334         /**
37335          * @event panelactivated
37336          * Fires when a panel is activated. 
37337          * @param {Roo.LayoutRegion} this
37338          * @param {Roo.ContentPanel} panel The activated panel
37339          */
37340         "panelactivated" : true,
37341         /**
37342          * @event resized
37343          * Fires when the user resizes this region. 
37344          * @param {Roo.LayoutRegion} this
37345          * @param {Number} newSize The new size (width for east/west, height for north/south)
37346          */
37347         "resized" : true
37348     };
37349     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37350     this.panels = new Roo.util.MixedCollection();
37351     this.panels.getKey = this.getPanelId.createDelegate(this);
37352     this.box = null;
37353     this.activePanel = null;
37354     // ensure listeners are added...
37355     
37356     if (config.listeners || config.events) {
37357         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37358             listeners : config.listeners || {},
37359             events : config.events || {}
37360         });
37361     }
37362     
37363     if(skipConfig !== true){
37364         this.applyConfig(config);
37365     }
37366 };
37367
37368 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37369 {
37370     getPanelId : function(p){
37371         return p.getId();
37372     },
37373     
37374     applyConfig : function(config){
37375         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37376         this.config = config;
37377         
37378     },
37379     
37380     /**
37381      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37382      * the width, for horizontal (north, south) the height.
37383      * @param {Number} newSize The new width or height
37384      */
37385     resizeTo : function(newSize){
37386         var el = this.el ? this.el :
37387                  (this.activePanel ? this.activePanel.getEl() : null);
37388         if(el){
37389             switch(this.position){
37390                 case "east":
37391                 case "west":
37392                     el.setWidth(newSize);
37393                     this.fireEvent("resized", this, newSize);
37394                 break;
37395                 case "north":
37396                 case "south":
37397                     el.setHeight(newSize);
37398                     this.fireEvent("resized", this, newSize);
37399                 break;                
37400             }
37401         }
37402     },
37403     
37404     getBox : function(){
37405         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37406     },
37407     
37408     getMargins : function(){
37409         return this.margins;
37410     },
37411     
37412     updateBox : function(box){
37413         this.box = box;
37414         var el = this.activePanel.getEl();
37415         el.dom.style.left = box.x + "px";
37416         el.dom.style.top = box.y + "px";
37417         this.activePanel.setSize(box.width, box.height);
37418     },
37419     
37420     /**
37421      * Returns the container element for this region.
37422      * @return {Roo.Element}
37423      */
37424     getEl : function(){
37425         return this.activePanel;
37426     },
37427     
37428     /**
37429      * Returns true if this region is currently visible.
37430      * @return {Boolean}
37431      */
37432     isVisible : function(){
37433         return this.activePanel ? true : false;
37434     },
37435     
37436     setActivePanel : function(panel){
37437         panel = this.getPanel(panel);
37438         if(this.activePanel && this.activePanel != panel){
37439             this.activePanel.setActiveState(false);
37440             this.activePanel.getEl().setLeftTop(-10000,-10000);
37441         }
37442         this.activePanel = panel;
37443         panel.setActiveState(true);
37444         if(this.box){
37445             panel.setSize(this.box.width, this.box.height);
37446         }
37447         this.fireEvent("panelactivated", this, panel);
37448         this.fireEvent("invalidated");
37449     },
37450     
37451     /**
37452      * Show the specified panel.
37453      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37454      * @return {Roo.ContentPanel} The shown panel or null
37455      */
37456     showPanel : function(panel){
37457         panel = this.getPanel(panel);
37458         if(panel){
37459             this.setActivePanel(panel);
37460         }
37461         return panel;
37462     },
37463     
37464     /**
37465      * Get the active panel for this region.
37466      * @return {Roo.ContentPanel} The active panel or null
37467      */
37468     getActivePanel : function(){
37469         return this.activePanel;
37470     },
37471     
37472     /**
37473      * Add the passed ContentPanel(s)
37474      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37475      * @return {Roo.ContentPanel} The panel added (if only one was added)
37476      */
37477     add : function(panel){
37478         if(arguments.length > 1){
37479             for(var i = 0, len = arguments.length; i < len; i++) {
37480                 this.add(arguments[i]);
37481             }
37482             return null;
37483         }
37484         if(this.hasPanel(panel)){
37485             this.showPanel(panel);
37486             return panel;
37487         }
37488         var el = panel.getEl();
37489         if(el.dom.parentNode != this.mgr.el.dom){
37490             this.mgr.el.dom.appendChild(el.dom);
37491         }
37492         if(panel.setRegion){
37493             panel.setRegion(this);
37494         }
37495         this.panels.add(panel);
37496         el.setStyle("position", "absolute");
37497         if(!panel.background){
37498             this.setActivePanel(panel);
37499             if(this.config.initialSize && this.panels.getCount()==1){
37500                 this.resizeTo(this.config.initialSize);
37501             }
37502         }
37503         this.fireEvent("paneladded", this, panel);
37504         return panel;
37505     },
37506     
37507     /**
37508      * Returns true if the panel is in this region.
37509      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37510      * @return {Boolean}
37511      */
37512     hasPanel : function(panel){
37513         if(typeof panel == "object"){ // must be panel obj
37514             panel = panel.getId();
37515         }
37516         return this.getPanel(panel) ? true : false;
37517     },
37518     
37519     /**
37520      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37521      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37522      * @param {Boolean} preservePanel Overrides the config preservePanel option
37523      * @return {Roo.ContentPanel} The panel that was removed
37524      */
37525     remove : function(panel, preservePanel){
37526         panel = this.getPanel(panel);
37527         if(!panel){
37528             return null;
37529         }
37530         var e = {};
37531         this.fireEvent("beforeremove", this, panel, e);
37532         if(e.cancel === true){
37533             return null;
37534         }
37535         var panelId = panel.getId();
37536         this.panels.removeKey(panelId);
37537         return panel;
37538     },
37539     
37540     /**
37541      * Returns the panel specified or null if it's not in this region.
37542      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37543      * @return {Roo.ContentPanel}
37544      */
37545     getPanel : function(id){
37546         if(typeof id == "object"){ // must be panel obj
37547             return id;
37548         }
37549         return this.panels.get(id);
37550     },
37551     
37552     /**
37553      * Returns this regions position (north/south/east/west/center).
37554      * @return {String} 
37555      */
37556     getPosition: function(){
37557         return this.position;    
37558     }
37559 });/*
37560  * Based on:
37561  * Ext JS Library 1.1.1
37562  * Copyright(c) 2006-2007, Ext JS, LLC.
37563  *
37564  * Originally Released Under LGPL - original licence link has changed is not relivant.
37565  *
37566  * Fork - LGPL
37567  * <script type="text/javascript">
37568  */
37569  
37570 /**
37571  * @class Roo.bootstrap.layout.Region
37572  * @extends Roo.bootstrap.layout.Basic
37573  * This class represents a region in a layout manager.
37574  
37575  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37576  * @cfg {Object}    cmargins        Margins for the element when collapsed (defaults to: north/south {top: 2, left: 0, right:0, bottom: 2} or east/west {top: 0, left: 2, right:2, bottom: 0})
37577  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
37578  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
37579  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
37580  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
37581  * @cfg {String}    title           The title for the region (overrides panel titles)
37582  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
37583  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37584  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
37585  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37586  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
37587  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37588  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
37589  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
37590  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
37591  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
37592
37593  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
37594  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
37595  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
37596  * @cfg {Number}    width           For East/West panels
37597  * @cfg {Number}    height          For North/South panels
37598  * @cfg {Boolean}   split           To show the splitter
37599  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
37600  * 
37601  * @cfg {string}   cls             Extra CSS classes to add to region
37602  * 
37603  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37604  * @cfg {string}   region  the region that it inhabits..
37605  *
37606
37607  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
37608  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
37609
37610  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
37611  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
37612  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
37613  */
37614 Roo.bootstrap.layout.Region = function(config)
37615 {
37616     this.applyConfig(config);
37617
37618     var mgr = config.mgr;
37619     var pos = config.region;
37620     config.skipConfig = true;
37621     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37622     
37623     if (mgr.el) {
37624         this.onRender(mgr.el);   
37625     }
37626      
37627     this.visible = true;
37628     this.collapsed = false;
37629     this.unrendered_panels = [];
37630 };
37631
37632 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37633
37634     position: '', // set by wrapper (eg. north/south etc..)
37635     unrendered_panels : null,  // unrendered panels.
37636     
37637     tabPosition : false,
37638     
37639     mgr: false, // points to 'Border'
37640     
37641     
37642     createBody : function(){
37643         /** This region's body element 
37644         * @type Roo.Element */
37645         this.bodyEl = this.el.createChild({
37646                 tag: "div",
37647                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37648         });
37649     },
37650
37651     onRender: function(ctr, pos)
37652     {
37653         var dh = Roo.DomHelper;
37654         /** This region's container element 
37655         * @type Roo.Element */
37656         this.el = dh.append(ctr.dom, {
37657                 tag: "div",
37658                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37659             }, true);
37660         /** This region's title element 
37661         * @type Roo.Element */
37662     
37663         this.titleEl = dh.append(this.el.dom,  {
37664                 tag: "div",
37665                 unselectable: "on",
37666                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37667                 children:[
37668                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
37669                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37670                 ]
37671             }, true);
37672         
37673         this.titleEl.enableDisplayMode();
37674         /** This region's title text element 
37675         * @type HTMLElement */
37676         this.titleTextEl = this.titleEl.dom.firstChild;
37677         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37678         /*
37679         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37680         this.closeBtn.enableDisplayMode();
37681         this.closeBtn.on("click", this.closeClicked, this);
37682         this.closeBtn.hide();
37683     */
37684         this.createBody(this.config);
37685         if(this.config.hideWhenEmpty){
37686             this.hide();
37687             this.on("paneladded", this.validateVisibility, this);
37688             this.on("panelremoved", this.validateVisibility, this);
37689         }
37690         if(this.autoScroll){
37691             this.bodyEl.setStyle("overflow", "auto");
37692         }else{
37693             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37694         }
37695         //if(c.titlebar !== false){
37696             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37697                 this.titleEl.hide();
37698             }else{
37699                 this.titleEl.show();
37700                 if(this.config.title){
37701                     this.titleTextEl.innerHTML = this.config.title;
37702                 }
37703             }
37704         //}
37705         if(this.config.collapsed){
37706             this.collapse(true);
37707         }
37708         if(this.config.hidden){
37709             this.hide();
37710         }
37711         
37712         if (this.unrendered_panels && this.unrendered_panels.length) {
37713             for (var i =0;i< this.unrendered_panels.length; i++) {
37714                 this.add(this.unrendered_panels[i]);
37715             }
37716             this.unrendered_panels = null;
37717             
37718         }
37719         
37720     },
37721     
37722     applyConfig : function(c)
37723     {
37724         /*
37725          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37726             var dh = Roo.DomHelper;
37727             if(c.titlebar !== false){
37728                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37729                 this.collapseBtn.on("click", this.collapse, this);
37730                 this.collapseBtn.enableDisplayMode();
37731                 /*
37732                 if(c.showPin === true || this.showPin){
37733                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37734                     this.stickBtn.enableDisplayMode();
37735                     this.stickBtn.on("click", this.expand, this);
37736                     this.stickBtn.hide();
37737                 }
37738                 
37739             }
37740             */
37741             /** This region's collapsed element
37742             * @type Roo.Element */
37743             /*
37744              *
37745             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37746                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37747             ]}, true);
37748             
37749             if(c.floatable !== false){
37750                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37751                this.collapsedEl.on("click", this.collapseClick, this);
37752             }
37753
37754             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37755                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37756                    id: "message", unselectable: "on", style:{"float":"left"}});
37757                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37758              }
37759             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37760             this.expandBtn.on("click", this.expand, this);
37761             
37762         }
37763         
37764         if(this.collapseBtn){
37765             this.collapseBtn.setVisible(c.collapsible == true);
37766         }
37767         
37768         this.cmargins = c.cmargins || this.cmargins ||
37769                          (this.position == "west" || this.position == "east" ?
37770                              {top: 0, left: 2, right:2, bottom: 0} :
37771                              {top: 2, left: 0, right:0, bottom: 2});
37772         */
37773         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37774         
37775         
37776         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
37777         
37778         this.autoScroll = c.autoScroll || false;
37779         
37780         
37781        
37782         
37783         this.duration = c.duration || .30;
37784         this.slideDuration = c.slideDuration || .45;
37785         this.config = c;
37786        
37787     },
37788     /**
37789      * Returns true if this region is currently visible.
37790      * @return {Boolean}
37791      */
37792     isVisible : function(){
37793         return this.visible;
37794     },
37795
37796     /**
37797      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
37798      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
37799      */
37800     //setCollapsedTitle : function(title){
37801     //    title = title || "&#160;";
37802      //   if(this.collapsedTitleTextEl){
37803       //      this.collapsedTitleTextEl.innerHTML = title;
37804        // }
37805     //},
37806
37807     getBox : function(){
37808         var b;
37809       //  if(!this.collapsed){
37810             b = this.el.getBox(false, true);
37811        // }else{
37812           //  b = this.collapsedEl.getBox(false, true);
37813         //}
37814         return b;
37815     },
37816
37817     getMargins : function(){
37818         return this.margins;
37819         //return this.collapsed ? this.cmargins : this.margins;
37820     },
37821 /*
37822     highlight : function(){
37823         this.el.addClass("x-layout-panel-dragover");
37824     },
37825
37826     unhighlight : function(){
37827         this.el.removeClass("x-layout-panel-dragover");
37828     },
37829 */
37830     updateBox : function(box)
37831     {
37832         if (!this.bodyEl) {
37833             return; // not rendered yet..
37834         }
37835         
37836         this.box = box;
37837         if(!this.collapsed){
37838             this.el.dom.style.left = box.x + "px";
37839             this.el.dom.style.top = box.y + "px";
37840             this.updateBody(box.width, box.height);
37841         }else{
37842             this.collapsedEl.dom.style.left = box.x + "px";
37843             this.collapsedEl.dom.style.top = box.y + "px";
37844             this.collapsedEl.setSize(box.width, box.height);
37845         }
37846         if(this.tabs){
37847             this.tabs.autoSizeTabs();
37848         }
37849     },
37850
37851     updateBody : function(w, h)
37852     {
37853         if(w !== null){
37854             this.el.setWidth(w);
37855             w -= this.el.getBorderWidth("rl");
37856             if(this.config.adjustments){
37857                 w += this.config.adjustments[0];
37858             }
37859         }
37860         if(h !== null && h > 0){
37861             this.el.setHeight(h);
37862             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
37863             h -= this.el.getBorderWidth("tb");
37864             if(this.config.adjustments){
37865                 h += this.config.adjustments[1];
37866             }
37867             this.bodyEl.setHeight(h);
37868             if(this.tabs){
37869                 h = this.tabs.syncHeight(h);
37870             }
37871         }
37872         if(this.panelSize){
37873             w = w !== null ? w : this.panelSize.width;
37874             h = h !== null ? h : this.panelSize.height;
37875         }
37876         if(this.activePanel){
37877             var el = this.activePanel.getEl();
37878             w = w !== null ? w : el.getWidth();
37879             h = h !== null ? h : el.getHeight();
37880             this.panelSize = {width: w, height: h};
37881             this.activePanel.setSize(w, h);
37882         }
37883         if(Roo.isIE && this.tabs){
37884             this.tabs.el.repaint();
37885         }
37886     },
37887
37888     /**
37889      * Returns the container element for this region.
37890      * @return {Roo.Element}
37891      */
37892     getEl : function(){
37893         return this.el;
37894     },
37895
37896     /**
37897      * Hides this region.
37898      */
37899     hide : function(){
37900         //if(!this.collapsed){
37901             this.el.dom.style.left = "-2000px";
37902             this.el.hide();
37903         //}else{
37904          //   this.collapsedEl.dom.style.left = "-2000px";
37905          //   this.collapsedEl.hide();
37906        // }
37907         this.visible = false;
37908         this.fireEvent("visibilitychange", this, false);
37909     },
37910
37911     /**
37912      * Shows this region if it was previously hidden.
37913      */
37914     show : function(){
37915         //if(!this.collapsed){
37916             this.el.show();
37917         //}else{
37918         //    this.collapsedEl.show();
37919        // }
37920         this.visible = true;
37921         this.fireEvent("visibilitychange", this, true);
37922     },
37923 /*
37924     closeClicked : function(){
37925         if(this.activePanel){
37926             this.remove(this.activePanel);
37927         }
37928     },
37929
37930     collapseClick : function(e){
37931         if(this.isSlid){
37932            e.stopPropagation();
37933            this.slideIn();
37934         }else{
37935            e.stopPropagation();
37936            this.slideOut();
37937         }
37938     },
37939 */
37940     /**
37941      * Collapses this region.
37942      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
37943      */
37944     /*
37945     collapse : function(skipAnim, skipCheck = false){
37946         if(this.collapsed) {
37947             return;
37948         }
37949         
37950         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
37951             
37952             this.collapsed = true;
37953             if(this.split){
37954                 this.split.el.hide();
37955             }
37956             if(this.config.animate && skipAnim !== true){
37957                 this.fireEvent("invalidated", this);
37958                 this.animateCollapse();
37959             }else{
37960                 this.el.setLocation(-20000,-20000);
37961                 this.el.hide();
37962                 this.collapsedEl.show();
37963                 this.fireEvent("collapsed", this);
37964                 this.fireEvent("invalidated", this);
37965             }
37966         }
37967         
37968     },
37969 */
37970     animateCollapse : function(){
37971         // overridden
37972     },
37973
37974     /**
37975      * Expands this region if it was previously collapsed.
37976      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
37977      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
37978      */
37979     /*
37980     expand : function(e, skipAnim){
37981         if(e) {
37982             e.stopPropagation();
37983         }
37984         if(!this.collapsed || this.el.hasActiveFx()) {
37985             return;
37986         }
37987         if(this.isSlid){
37988             this.afterSlideIn();
37989             skipAnim = true;
37990         }
37991         this.collapsed = false;
37992         if(this.config.animate && skipAnim !== true){
37993             this.animateExpand();
37994         }else{
37995             this.el.show();
37996             if(this.split){
37997                 this.split.el.show();
37998             }
37999             this.collapsedEl.setLocation(-2000,-2000);
38000             this.collapsedEl.hide();
38001             this.fireEvent("invalidated", this);
38002             this.fireEvent("expanded", this);
38003         }
38004     },
38005 */
38006     animateExpand : function(){
38007         // overridden
38008     },
38009
38010     initTabs : function()
38011     {
38012         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38013         
38014         var ts = new Roo.bootstrap.panel.Tabs({
38015             el: this.bodyEl.dom,
38016             region : this,
38017             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38018             disableTooltips: this.config.disableTabTips,
38019             toolbar : this.config.toolbar
38020         });
38021         
38022         if(this.config.hideTabs){
38023             ts.stripWrap.setDisplayed(false);
38024         }
38025         this.tabs = ts;
38026         ts.resizeTabs = this.config.resizeTabs === true;
38027         ts.minTabWidth = this.config.minTabWidth || 40;
38028         ts.maxTabWidth = this.config.maxTabWidth || 250;
38029         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38030         ts.monitorResize = false;
38031         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38032         ts.bodyEl.addClass('roo-layout-tabs-body');
38033         this.panels.each(this.initPanelAsTab, this);
38034     },
38035
38036     initPanelAsTab : function(panel){
38037         var ti = this.tabs.addTab(
38038             panel.getEl().id,
38039             panel.getTitle(),
38040             null,
38041             this.config.closeOnTab && panel.isClosable(),
38042             panel.tpl
38043         );
38044         if(panel.tabTip !== undefined){
38045             ti.setTooltip(panel.tabTip);
38046         }
38047         ti.on("activate", function(){
38048               this.setActivePanel(panel);
38049         }, this);
38050         
38051         if(this.config.closeOnTab){
38052             ti.on("beforeclose", function(t, e){
38053                 e.cancel = true;
38054                 this.remove(panel);
38055             }, this);
38056         }
38057         
38058         panel.tabItem = ti;
38059         
38060         return ti;
38061     },
38062
38063     updatePanelTitle : function(panel, title)
38064     {
38065         if(this.activePanel == panel){
38066             this.updateTitle(title);
38067         }
38068         if(this.tabs){
38069             var ti = this.tabs.getTab(panel.getEl().id);
38070             ti.setText(title);
38071             if(panel.tabTip !== undefined){
38072                 ti.setTooltip(panel.tabTip);
38073             }
38074         }
38075     },
38076
38077     updateTitle : function(title){
38078         if(this.titleTextEl && !this.config.title){
38079             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38080         }
38081     },
38082
38083     setActivePanel : function(panel)
38084     {
38085         panel = this.getPanel(panel);
38086         if(this.activePanel && this.activePanel != panel){
38087             if(this.activePanel.setActiveState(false) === false){
38088                 return;
38089             }
38090         }
38091         this.activePanel = panel;
38092         panel.setActiveState(true);
38093         if(this.panelSize){
38094             panel.setSize(this.panelSize.width, this.panelSize.height);
38095         }
38096         if(this.closeBtn){
38097             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38098         }
38099         this.updateTitle(panel.getTitle());
38100         if(this.tabs){
38101             this.fireEvent("invalidated", this);
38102         }
38103         this.fireEvent("panelactivated", this, panel);
38104     },
38105
38106     /**
38107      * Shows the specified panel.
38108      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38109      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38110      */
38111     showPanel : function(panel)
38112     {
38113         panel = this.getPanel(panel);
38114         if(panel){
38115             if(this.tabs){
38116                 var tab = this.tabs.getTab(panel.getEl().id);
38117                 if(tab.isHidden()){
38118                     this.tabs.unhideTab(tab.id);
38119                 }
38120                 tab.activate();
38121             }else{
38122                 this.setActivePanel(panel);
38123             }
38124         }
38125         return panel;
38126     },
38127
38128     /**
38129      * Get the active panel for this region.
38130      * @return {Roo.ContentPanel} The active panel or null
38131      */
38132     getActivePanel : function(){
38133         return this.activePanel;
38134     },
38135
38136     validateVisibility : function(){
38137         if(this.panels.getCount() < 1){
38138             this.updateTitle("&#160;");
38139             this.closeBtn.hide();
38140             this.hide();
38141         }else{
38142             if(!this.isVisible()){
38143                 this.show();
38144             }
38145         }
38146     },
38147
38148     /**
38149      * Adds the passed ContentPanel(s) to this region.
38150      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38151      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38152      */
38153     add : function(panel)
38154     {
38155         if(arguments.length > 1){
38156             for(var i = 0, len = arguments.length; i < len; i++) {
38157                 this.add(arguments[i]);
38158             }
38159             return null;
38160         }
38161         
38162         // if we have not been rendered yet, then we can not really do much of this..
38163         if (!this.bodyEl) {
38164             this.unrendered_panels.push(panel);
38165             return panel;
38166         }
38167         
38168         
38169         
38170         
38171         if(this.hasPanel(panel)){
38172             this.showPanel(panel);
38173             return panel;
38174         }
38175         panel.setRegion(this);
38176         this.panels.add(panel);
38177        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38178             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38179             // and hide them... ???
38180             this.bodyEl.dom.appendChild(panel.getEl().dom);
38181             if(panel.background !== true){
38182                 this.setActivePanel(panel);
38183             }
38184             this.fireEvent("paneladded", this, panel);
38185             return panel;
38186         }
38187         */
38188         if(!this.tabs){
38189             this.initTabs();
38190         }else{
38191             this.initPanelAsTab(panel);
38192         }
38193         
38194         
38195         if(panel.background !== true){
38196             this.tabs.activate(panel.getEl().id);
38197         }
38198         this.fireEvent("paneladded", this, panel);
38199         return panel;
38200     },
38201
38202     /**
38203      * Hides the tab for the specified panel.
38204      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38205      */
38206     hidePanel : function(panel){
38207         if(this.tabs && (panel = this.getPanel(panel))){
38208             this.tabs.hideTab(panel.getEl().id);
38209         }
38210     },
38211
38212     /**
38213      * Unhides the tab for a previously hidden panel.
38214      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38215      */
38216     unhidePanel : function(panel){
38217         if(this.tabs && (panel = this.getPanel(panel))){
38218             this.tabs.unhideTab(panel.getEl().id);
38219         }
38220     },
38221
38222     clearPanels : function(){
38223         while(this.panels.getCount() > 0){
38224              this.remove(this.panels.first());
38225         }
38226     },
38227
38228     /**
38229      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38230      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38231      * @param {Boolean} preservePanel Overrides the config preservePanel option
38232      * @return {Roo.ContentPanel} The panel that was removed
38233      */
38234     remove : function(panel, preservePanel)
38235     {
38236         panel = this.getPanel(panel);
38237         if(!panel){
38238             return null;
38239         }
38240         var e = {};
38241         this.fireEvent("beforeremove", this, panel, e);
38242         if(e.cancel === true){
38243             return null;
38244         }
38245         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38246         var panelId = panel.getId();
38247         this.panels.removeKey(panelId);
38248         if(preservePanel){
38249             document.body.appendChild(panel.getEl().dom);
38250         }
38251         if(this.tabs){
38252             this.tabs.removeTab(panel.getEl().id);
38253         }else if (!preservePanel){
38254             this.bodyEl.dom.removeChild(panel.getEl().dom);
38255         }
38256         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38257             var p = this.panels.first();
38258             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38259             tempEl.appendChild(p.getEl().dom);
38260             this.bodyEl.update("");
38261             this.bodyEl.dom.appendChild(p.getEl().dom);
38262             tempEl = null;
38263             this.updateTitle(p.getTitle());
38264             this.tabs = null;
38265             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38266             this.setActivePanel(p);
38267         }
38268         panel.setRegion(null);
38269         if(this.activePanel == panel){
38270             this.activePanel = null;
38271         }
38272         if(this.config.autoDestroy !== false && preservePanel !== true){
38273             try{panel.destroy();}catch(e){}
38274         }
38275         this.fireEvent("panelremoved", this, panel);
38276         return panel;
38277     },
38278
38279     /**
38280      * Returns the TabPanel component used by this region
38281      * @return {Roo.TabPanel}
38282      */
38283     getTabs : function(){
38284         return this.tabs;
38285     },
38286
38287     createTool : function(parentEl, className){
38288         var btn = Roo.DomHelper.append(parentEl, {
38289             tag: "div",
38290             cls: "x-layout-tools-button",
38291             children: [ {
38292                 tag: "div",
38293                 cls: "roo-layout-tools-button-inner " + className,
38294                 html: "&#160;"
38295             }]
38296         }, true);
38297         btn.addClassOnOver("roo-layout-tools-button-over");
38298         return btn;
38299     }
38300 });/*
38301  * Based on:
38302  * Ext JS Library 1.1.1
38303  * Copyright(c) 2006-2007, Ext JS, LLC.
38304  *
38305  * Originally Released Under LGPL - original licence link has changed is not relivant.
38306  *
38307  * Fork - LGPL
38308  * <script type="text/javascript">
38309  */
38310  
38311
38312
38313 /**
38314  * @class Roo.SplitLayoutRegion
38315  * @extends Roo.LayoutRegion
38316  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38317  */
38318 Roo.bootstrap.layout.Split = function(config){
38319     this.cursor = config.cursor;
38320     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38321 };
38322
38323 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38324 {
38325     splitTip : "Drag to resize.",
38326     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38327     useSplitTips : false,
38328
38329     applyConfig : function(config){
38330         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38331     },
38332     
38333     onRender : function(ctr,pos) {
38334         
38335         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38336         if(!this.config.split){
38337             return;
38338         }
38339         if(!this.split){
38340             
38341             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38342                             tag: "div",
38343                             id: this.el.id + "-split",
38344                             cls: "roo-layout-split roo-layout-split-"+this.position,
38345                             html: "&#160;"
38346             });
38347             /** The SplitBar for this region 
38348             * @type Roo.SplitBar */
38349             // does not exist yet...
38350             Roo.log([this.position, this.orientation]);
38351             
38352             this.split = new Roo.bootstrap.SplitBar({
38353                 dragElement : splitEl,
38354                 resizingElement: this.el,
38355                 orientation : this.orientation
38356             });
38357             
38358             this.split.on("moved", this.onSplitMove, this);
38359             this.split.useShim = this.config.useShim === true;
38360             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38361             if(this.useSplitTips){
38362                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38363             }
38364             //if(config.collapsible){
38365             //    this.split.el.on("dblclick", this.collapse,  this);
38366             //}
38367         }
38368         if(typeof this.config.minSize != "undefined"){
38369             this.split.minSize = this.config.minSize;
38370         }
38371         if(typeof this.config.maxSize != "undefined"){
38372             this.split.maxSize = this.config.maxSize;
38373         }
38374         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38375             this.hideSplitter();
38376         }
38377         
38378     },
38379
38380     getHMaxSize : function(){
38381          var cmax = this.config.maxSize || 10000;
38382          var center = this.mgr.getRegion("center");
38383          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38384     },
38385
38386     getVMaxSize : function(){
38387          var cmax = this.config.maxSize || 10000;
38388          var center = this.mgr.getRegion("center");
38389          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38390     },
38391
38392     onSplitMove : function(split, newSize){
38393         this.fireEvent("resized", this, newSize);
38394     },
38395     
38396     /** 
38397      * Returns the {@link Roo.SplitBar} for this region.
38398      * @return {Roo.SplitBar}
38399      */
38400     getSplitBar : function(){
38401         return this.split;
38402     },
38403     
38404     hide : function(){
38405         this.hideSplitter();
38406         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38407     },
38408
38409     hideSplitter : function(){
38410         if(this.split){
38411             this.split.el.setLocation(-2000,-2000);
38412             this.split.el.hide();
38413         }
38414     },
38415
38416     show : function(){
38417         if(this.split){
38418             this.split.el.show();
38419         }
38420         Roo.bootstrap.layout.Split.superclass.show.call(this);
38421     },
38422     
38423     beforeSlide: function(){
38424         if(Roo.isGecko){// firefox overflow auto bug workaround
38425             this.bodyEl.clip();
38426             if(this.tabs) {
38427                 this.tabs.bodyEl.clip();
38428             }
38429             if(this.activePanel){
38430                 this.activePanel.getEl().clip();
38431                 
38432                 if(this.activePanel.beforeSlide){
38433                     this.activePanel.beforeSlide();
38434                 }
38435             }
38436         }
38437     },
38438     
38439     afterSlide : function(){
38440         if(Roo.isGecko){// firefox overflow auto bug workaround
38441             this.bodyEl.unclip();
38442             if(this.tabs) {
38443                 this.tabs.bodyEl.unclip();
38444             }
38445             if(this.activePanel){
38446                 this.activePanel.getEl().unclip();
38447                 if(this.activePanel.afterSlide){
38448                     this.activePanel.afterSlide();
38449                 }
38450             }
38451         }
38452     },
38453
38454     initAutoHide : function(){
38455         if(this.autoHide !== false){
38456             if(!this.autoHideHd){
38457                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38458                 this.autoHideHd = {
38459                     "mouseout": function(e){
38460                         if(!e.within(this.el, true)){
38461                             st.delay(500);
38462                         }
38463                     },
38464                     "mouseover" : function(e){
38465                         st.cancel();
38466                     },
38467                     scope : this
38468                 };
38469             }
38470             this.el.on(this.autoHideHd);
38471         }
38472     },
38473
38474     clearAutoHide : function(){
38475         if(this.autoHide !== false){
38476             this.el.un("mouseout", this.autoHideHd.mouseout);
38477             this.el.un("mouseover", this.autoHideHd.mouseover);
38478         }
38479     },
38480
38481     clearMonitor : function(){
38482         Roo.get(document).un("click", this.slideInIf, this);
38483     },
38484
38485     // these names are backwards but not changed for compat
38486     slideOut : function(){
38487         if(this.isSlid || this.el.hasActiveFx()){
38488             return;
38489         }
38490         this.isSlid = true;
38491         if(this.collapseBtn){
38492             this.collapseBtn.hide();
38493         }
38494         this.closeBtnState = this.closeBtn.getStyle('display');
38495         this.closeBtn.hide();
38496         if(this.stickBtn){
38497             this.stickBtn.show();
38498         }
38499         this.el.show();
38500         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38501         this.beforeSlide();
38502         this.el.setStyle("z-index", 10001);
38503         this.el.slideIn(this.getSlideAnchor(), {
38504             callback: function(){
38505                 this.afterSlide();
38506                 this.initAutoHide();
38507                 Roo.get(document).on("click", this.slideInIf, this);
38508                 this.fireEvent("slideshow", this);
38509             },
38510             scope: this,
38511             block: true
38512         });
38513     },
38514
38515     afterSlideIn : function(){
38516         this.clearAutoHide();
38517         this.isSlid = false;
38518         this.clearMonitor();
38519         this.el.setStyle("z-index", "");
38520         if(this.collapseBtn){
38521             this.collapseBtn.show();
38522         }
38523         this.closeBtn.setStyle('display', this.closeBtnState);
38524         if(this.stickBtn){
38525             this.stickBtn.hide();
38526         }
38527         this.fireEvent("slidehide", this);
38528     },
38529
38530     slideIn : function(cb){
38531         if(!this.isSlid || this.el.hasActiveFx()){
38532             Roo.callback(cb);
38533             return;
38534         }
38535         this.isSlid = false;
38536         this.beforeSlide();
38537         this.el.slideOut(this.getSlideAnchor(), {
38538             callback: function(){
38539                 this.el.setLeftTop(-10000, -10000);
38540                 this.afterSlide();
38541                 this.afterSlideIn();
38542                 Roo.callback(cb);
38543             },
38544             scope: this,
38545             block: true
38546         });
38547     },
38548     
38549     slideInIf : function(e){
38550         if(!e.within(this.el)){
38551             this.slideIn();
38552         }
38553     },
38554
38555     animateCollapse : function(){
38556         this.beforeSlide();
38557         this.el.setStyle("z-index", 20000);
38558         var anchor = this.getSlideAnchor();
38559         this.el.slideOut(anchor, {
38560             callback : function(){
38561                 this.el.setStyle("z-index", "");
38562                 this.collapsedEl.slideIn(anchor, {duration:.3});
38563                 this.afterSlide();
38564                 this.el.setLocation(-10000,-10000);
38565                 this.el.hide();
38566                 this.fireEvent("collapsed", this);
38567             },
38568             scope: this,
38569             block: true
38570         });
38571     },
38572
38573     animateExpand : function(){
38574         this.beforeSlide();
38575         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38576         this.el.setStyle("z-index", 20000);
38577         this.collapsedEl.hide({
38578             duration:.1
38579         });
38580         this.el.slideIn(this.getSlideAnchor(), {
38581             callback : function(){
38582                 this.el.setStyle("z-index", "");
38583                 this.afterSlide();
38584                 if(this.split){
38585                     this.split.el.show();
38586                 }
38587                 this.fireEvent("invalidated", this);
38588                 this.fireEvent("expanded", this);
38589             },
38590             scope: this,
38591             block: true
38592         });
38593     },
38594
38595     anchors : {
38596         "west" : "left",
38597         "east" : "right",
38598         "north" : "top",
38599         "south" : "bottom"
38600     },
38601
38602     sanchors : {
38603         "west" : "l",
38604         "east" : "r",
38605         "north" : "t",
38606         "south" : "b"
38607     },
38608
38609     canchors : {
38610         "west" : "tl-tr",
38611         "east" : "tr-tl",
38612         "north" : "tl-bl",
38613         "south" : "bl-tl"
38614     },
38615
38616     getAnchor : function(){
38617         return this.anchors[this.position];
38618     },
38619
38620     getCollapseAnchor : function(){
38621         return this.canchors[this.position];
38622     },
38623
38624     getSlideAnchor : function(){
38625         return this.sanchors[this.position];
38626     },
38627
38628     getAlignAdj : function(){
38629         var cm = this.cmargins;
38630         switch(this.position){
38631             case "west":
38632                 return [0, 0];
38633             break;
38634             case "east":
38635                 return [0, 0];
38636             break;
38637             case "north":
38638                 return [0, 0];
38639             break;
38640             case "south":
38641                 return [0, 0];
38642             break;
38643         }
38644     },
38645
38646     getExpandAdj : function(){
38647         var c = this.collapsedEl, cm = this.cmargins;
38648         switch(this.position){
38649             case "west":
38650                 return [-(cm.right+c.getWidth()+cm.left), 0];
38651             break;
38652             case "east":
38653                 return [cm.right+c.getWidth()+cm.left, 0];
38654             break;
38655             case "north":
38656                 return [0, -(cm.top+cm.bottom+c.getHeight())];
38657             break;
38658             case "south":
38659                 return [0, cm.top+cm.bottom+c.getHeight()];
38660             break;
38661         }
38662     }
38663 });/*
38664  * Based on:
38665  * Ext JS Library 1.1.1
38666  * Copyright(c) 2006-2007, Ext JS, LLC.
38667  *
38668  * Originally Released Under LGPL - original licence link has changed is not relivant.
38669  *
38670  * Fork - LGPL
38671  * <script type="text/javascript">
38672  */
38673 /*
38674  * These classes are private internal classes
38675  */
38676 Roo.bootstrap.layout.Center = function(config){
38677     config.region = "center";
38678     Roo.bootstrap.layout.Region.call(this, config);
38679     this.visible = true;
38680     this.minWidth = config.minWidth || 20;
38681     this.minHeight = config.minHeight || 20;
38682 };
38683
38684 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38685     hide : function(){
38686         // center panel can't be hidden
38687     },
38688     
38689     show : function(){
38690         // center panel can't be hidden
38691     },
38692     
38693     getMinWidth: function(){
38694         return this.minWidth;
38695     },
38696     
38697     getMinHeight: function(){
38698         return this.minHeight;
38699     }
38700 });
38701
38702
38703
38704
38705  
38706
38707
38708
38709
38710
38711
38712 Roo.bootstrap.layout.North = function(config)
38713 {
38714     config.region = 'north';
38715     config.cursor = 'n-resize';
38716     
38717     Roo.bootstrap.layout.Split.call(this, config);
38718     
38719     
38720     if(this.split){
38721         this.split.placement = Roo.bootstrap.SplitBar.TOP;
38722         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38723         this.split.el.addClass("roo-layout-split-v");
38724     }
38725     var size = config.initialSize || config.height;
38726     if(typeof size != "undefined"){
38727         this.el.setHeight(size);
38728     }
38729 };
38730 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38731 {
38732     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38733     
38734     
38735     
38736     getBox : function(){
38737         if(this.collapsed){
38738             return this.collapsedEl.getBox();
38739         }
38740         var box = this.el.getBox();
38741         if(this.split){
38742             box.height += this.split.el.getHeight();
38743         }
38744         return box;
38745     },
38746     
38747     updateBox : function(box){
38748         if(this.split && !this.collapsed){
38749             box.height -= this.split.el.getHeight();
38750             this.split.el.setLeft(box.x);
38751             this.split.el.setTop(box.y+box.height);
38752             this.split.el.setWidth(box.width);
38753         }
38754         if(this.collapsed){
38755             this.updateBody(box.width, null);
38756         }
38757         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38758     }
38759 });
38760
38761
38762
38763
38764
38765 Roo.bootstrap.layout.South = function(config){
38766     config.region = 'south';
38767     config.cursor = 's-resize';
38768     Roo.bootstrap.layout.Split.call(this, config);
38769     if(this.split){
38770         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38771         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38772         this.split.el.addClass("roo-layout-split-v");
38773     }
38774     var size = config.initialSize || config.height;
38775     if(typeof size != "undefined"){
38776         this.el.setHeight(size);
38777     }
38778 };
38779
38780 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
38781     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38782     getBox : function(){
38783         if(this.collapsed){
38784             return this.collapsedEl.getBox();
38785         }
38786         var box = this.el.getBox();
38787         if(this.split){
38788             var sh = this.split.el.getHeight();
38789             box.height += sh;
38790             box.y -= sh;
38791         }
38792         return box;
38793     },
38794     
38795     updateBox : function(box){
38796         if(this.split && !this.collapsed){
38797             var sh = this.split.el.getHeight();
38798             box.height -= sh;
38799             box.y += sh;
38800             this.split.el.setLeft(box.x);
38801             this.split.el.setTop(box.y-sh);
38802             this.split.el.setWidth(box.width);
38803         }
38804         if(this.collapsed){
38805             this.updateBody(box.width, null);
38806         }
38807         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38808     }
38809 });
38810
38811 Roo.bootstrap.layout.East = function(config){
38812     config.region = "east";
38813     config.cursor = "e-resize";
38814     Roo.bootstrap.layout.Split.call(this, config);
38815     if(this.split){
38816         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
38817         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38818         this.split.el.addClass("roo-layout-split-h");
38819     }
38820     var size = config.initialSize || config.width;
38821     if(typeof size != "undefined"){
38822         this.el.setWidth(size);
38823     }
38824 };
38825 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
38826     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38827     getBox : function(){
38828         if(this.collapsed){
38829             return this.collapsedEl.getBox();
38830         }
38831         var box = this.el.getBox();
38832         if(this.split){
38833             var sw = this.split.el.getWidth();
38834             box.width += sw;
38835             box.x -= sw;
38836         }
38837         return box;
38838     },
38839
38840     updateBox : function(box){
38841         if(this.split && !this.collapsed){
38842             var sw = this.split.el.getWidth();
38843             box.width -= sw;
38844             this.split.el.setLeft(box.x);
38845             this.split.el.setTop(box.y);
38846             this.split.el.setHeight(box.height);
38847             box.x += sw;
38848         }
38849         if(this.collapsed){
38850             this.updateBody(null, box.height);
38851         }
38852         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38853     }
38854 });
38855
38856 Roo.bootstrap.layout.West = function(config){
38857     config.region = "west";
38858     config.cursor = "w-resize";
38859     
38860     Roo.bootstrap.layout.Split.call(this, config);
38861     if(this.split){
38862         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
38863         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38864         this.split.el.addClass("roo-layout-split-h");
38865     }
38866     
38867 };
38868 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
38869     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38870     
38871     onRender: function(ctr, pos)
38872     {
38873         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
38874         var size = this.config.initialSize || this.config.width;
38875         if(typeof size != "undefined"){
38876             this.el.setWidth(size);
38877         }
38878     },
38879     
38880     getBox : function(){
38881         if(this.collapsed){
38882             return this.collapsedEl.getBox();
38883         }
38884         var box = this.el.getBox();
38885         if(this.split){
38886             box.width += this.split.el.getWidth();
38887         }
38888         return box;
38889     },
38890     
38891     updateBox : function(box){
38892         if(this.split && !this.collapsed){
38893             var sw = this.split.el.getWidth();
38894             box.width -= sw;
38895             this.split.el.setLeft(box.x+box.width);
38896             this.split.el.setTop(box.y);
38897             this.split.el.setHeight(box.height);
38898         }
38899         if(this.collapsed){
38900             this.updateBody(null, box.height);
38901         }
38902         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38903     }
38904 });Roo.namespace("Roo.bootstrap.panel");/*
38905  * Based on:
38906  * Ext JS Library 1.1.1
38907  * Copyright(c) 2006-2007, Ext JS, LLC.
38908  *
38909  * Originally Released Under LGPL - original licence link has changed is not relivant.
38910  *
38911  * Fork - LGPL
38912  * <script type="text/javascript">
38913  */
38914 /**
38915  * @class Roo.ContentPanel
38916  * @extends Roo.util.Observable
38917  * A basic ContentPanel element.
38918  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
38919  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
38920  * @cfg {Boolean/Object} autoCreate True to auto generate the DOM element for this panel, or a {@link Roo.DomHelper} config of the element to create
38921  * @cfg {Boolean}   closable      True if the panel can be closed/removed
38922  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
38923  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
38924  * @cfg {Toolbar}   toolbar       A toolbar for this panel
38925  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
38926  * @cfg {String} title          The title for this panel
38927  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
38928  * @cfg {String} url            Calls {@link #setUrl} with this value
38929  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
38930  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
38931  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
38932  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
38933  * @cfg {Boolean} badges render the badges
38934
38935  * @constructor
38936  * Create a new ContentPanel.
38937  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
38938  * @param {String/Object} config A string to set only the title or a config object
38939  * @param {String} content (optional) Set the HTML content for this panel
38940  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
38941  */
38942 Roo.bootstrap.panel.Content = function( config){
38943     
38944     this.tpl = config.tpl || false;
38945     
38946     var el = config.el;
38947     var content = config.content;
38948
38949     if(config.autoCreate){ // xtype is available if this is called from factory
38950         el = Roo.id();
38951     }
38952     this.el = Roo.get(el);
38953     if(!this.el && config && config.autoCreate){
38954         if(typeof config.autoCreate == "object"){
38955             if(!config.autoCreate.id){
38956                 config.autoCreate.id = config.id||el;
38957             }
38958             this.el = Roo.DomHelper.append(document.body,
38959                         config.autoCreate, true);
38960         }else{
38961             var elcfg =  {   tag: "div",
38962                             cls: "roo-layout-inactive-content",
38963                             id: config.id||el
38964                             };
38965             if (config.html) {
38966                 elcfg.html = config.html;
38967                 
38968             }
38969                         
38970             this.el = Roo.DomHelper.append(document.body, elcfg , true);
38971         }
38972     } 
38973     this.closable = false;
38974     this.loaded = false;
38975     this.active = false;
38976    
38977       
38978     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
38979         
38980         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
38981         
38982         this.wrapEl = this.el; //this.el.wrap();
38983         var ti = [];
38984         if (config.toolbar.items) {
38985             ti = config.toolbar.items ;
38986             delete config.toolbar.items ;
38987         }
38988         
38989         var nitems = [];
38990         this.toolbar.render(this.wrapEl, 'before');
38991         for(var i =0;i < ti.length;i++) {
38992           //  Roo.log(['add child', items[i]]);
38993             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38994         }
38995         this.toolbar.items = nitems;
38996         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
38997         delete config.toolbar;
38998         
38999     }
39000     /*
39001     // xtype created footer. - not sure if will work as we normally have to render first..
39002     if (this.footer && !this.footer.el && this.footer.xtype) {
39003         if (!this.wrapEl) {
39004             this.wrapEl = this.el.wrap();
39005         }
39006     
39007         this.footer.container = this.wrapEl.createChild();
39008          
39009         this.footer = Roo.factory(this.footer, Roo);
39010         
39011     }
39012     */
39013     
39014      if(typeof config == "string"){
39015         this.title = config;
39016     }else{
39017         Roo.apply(this, config);
39018     }
39019     
39020     if(this.resizeEl){
39021         this.resizeEl = Roo.get(this.resizeEl, true);
39022     }else{
39023         this.resizeEl = this.el;
39024     }
39025     // handle view.xtype
39026     
39027  
39028     
39029     
39030     this.addEvents({
39031         /**
39032          * @event activate
39033          * Fires when this panel is activated. 
39034          * @param {Roo.ContentPanel} this
39035          */
39036         "activate" : true,
39037         /**
39038          * @event deactivate
39039          * Fires when this panel is activated. 
39040          * @param {Roo.ContentPanel} this
39041          */
39042         "deactivate" : true,
39043
39044         /**
39045          * @event resize
39046          * Fires when this panel is resized if fitToFrame is true.
39047          * @param {Roo.ContentPanel} this
39048          * @param {Number} width The width after any component adjustments
39049          * @param {Number} height The height after any component adjustments
39050          */
39051         "resize" : true,
39052         
39053          /**
39054          * @event render
39055          * Fires when this tab is created
39056          * @param {Roo.ContentPanel} this
39057          */
39058         "render" : true
39059         
39060         
39061         
39062     });
39063     
39064
39065     
39066     
39067     if(this.autoScroll){
39068         this.resizeEl.setStyle("overflow", "auto");
39069     } else {
39070         // fix randome scrolling
39071         //this.el.on('scroll', function() {
39072         //    Roo.log('fix random scolling');
39073         //    this.scrollTo('top',0); 
39074         //});
39075     }
39076     content = content || this.content;
39077     if(content){
39078         this.setContent(content);
39079     }
39080     if(config && config.url){
39081         this.setUrl(this.url, this.params, this.loadOnce);
39082     }
39083     
39084     
39085     
39086     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39087     
39088     if (this.view && typeof(this.view.xtype) != 'undefined') {
39089         this.view.el = this.el.appendChild(document.createElement("div"));
39090         this.view = Roo.factory(this.view); 
39091         this.view.render  &&  this.view.render(false, '');  
39092     }
39093     
39094     
39095     this.fireEvent('render', this);
39096 };
39097
39098 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39099     
39100     tabTip : '',
39101     
39102     setRegion : function(region){
39103         this.region = region;
39104         this.setActiveClass(region && !this.background);
39105     },
39106     
39107     
39108     setActiveClass: function(state)
39109     {
39110         if(state){
39111            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39112            this.el.setStyle('position','relative');
39113         }else{
39114            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39115            this.el.setStyle('position', 'absolute');
39116         } 
39117     },
39118     
39119     /**
39120      * Returns the toolbar for this Panel if one was configured. 
39121      * @return {Roo.Toolbar} 
39122      */
39123     getToolbar : function(){
39124         return this.toolbar;
39125     },
39126     
39127     setActiveState : function(active)
39128     {
39129         this.active = active;
39130         this.setActiveClass(active);
39131         if(!active){
39132             if(this.fireEvent("deactivate", this) === false){
39133                 return false;
39134             }
39135             return true;
39136         }
39137         this.fireEvent("activate", this);
39138         return true;
39139     },
39140     /**
39141      * Updates this panel's element
39142      * @param {String} content The new content
39143      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39144     */
39145     setContent : function(content, loadScripts){
39146         this.el.update(content, loadScripts);
39147     },
39148
39149     ignoreResize : function(w, h){
39150         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39151             return true;
39152         }else{
39153             this.lastSize = {width: w, height: h};
39154             return false;
39155         }
39156     },
39157     /**
39158      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39159      * @return {Roo.UpdateManager} The UpdateManager
39160      */
39161     getUpdateManager : function(){
39162         return this.el.getUpdateManager();
39163     },
39164      /**
39165      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39166      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
39167 <pre><code>
39168 panel.load({
39169     url: "your-url.php",
39170     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39171     callback: yourFunction,
39172     scope: yourObject, //(optional scope)
39173     discardUrl: false,
39174     nocache: false,
39175     text: "Loading...",
39176     timeout: 30,
39177     scripts: false
39178 });
39179 </code></pre>
39180      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39181      * are shorthand for <i>disableCaching</i>, <i>indicatorText</i> and <i>loadScripts</i> and are used to set their associated property on this panel UpdateManager instance.
39182      * @param {String/Object} params (optional) The parameters to pass as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
39183      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39184      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
39185      * @return {Roo.ContentPanel} this
39186      */
39187     load : function(){
39188         var um = this.el.getUpdateManager();
39189         um.update.apply(um, arguments);
39190         return this;
39191     },
39192
39193
39194     /**
39195      * Set a URL to be used to load the content for this panel. When this panel is activated, the content will be loaded from that URL.
39196      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39197      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
39198      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this panel is activated. (Defaults to false)
39199      * @return {Roo.UpdateManager} The UpdateManager
39200      */
39201     setUrl : function(url, params, loadOnce){
39202         if(this.refreshDelegate){
39203             this.removeListener("activate", this.refreshDelegate);
39204         }
39205         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39206         this.on("activate", this.refreshDelegate);
39207         return this.el.getUpdateManager();
39208     },
39209     
39210     _handleRefresh : function(url, params, loadOnce){
39211         if(!loadOnce || !this.loaded){
39212             var updater = this.el.getUpdateManager();
39213             updater.update(url, params, this._setLoaded.createDelegate(this));
39214         }
39215     },
39216     
39217     _setLoaded : function(){
39218         this.loaded = true;
39219     }, 
39220     
39221     /**
39222      * Returns this panel's id
39223      * @return {String} 
39224      */
39225     getId : function(){
39226         return this.el.id;
39227     },
39228     
39229     /** 
39230      * Returns this panel's element - used by regiosn to add.
39231      * @return {Roo.Element} 
39232      */
39233     getEl : function(){
39234         return this.wrapEl || this.el;
39235     },
39236     
39237    
39238     
39239     adjustForComponents : function(width, height)
39240     {
39241         //Roo.log('adjustForComponents ');
39242         if(this.resizeEl != this.el){
39243             width -= this.el.getFrameWidth('lr');
39244             height -= this.el.getFrameWidth('tb');
39245         }
39246         if(this.toolbar){
39247             var te = this.toolbar.getEl();
39248             te.setWidth(width);
39249             height -= te.getHeight();
39250         }
39251         if(this.footer){
39252             var te = this.footer.getEl();
39253             te.setWidth(width);
39254             height -= te.getHeight();
39255         }
39256         
39257         
39258         if(this.adjustments){
39259             width += this.adjustments[0];
39260             height += this.adjustments[1];
39261         }
39262         return {"width": width, "height": height};
39263     },
39264     
39265     setSize : function(width, height){
39266         if(this.fitToFrame && !this.ignoreResize(width, height)){
39267             if(this.fitContainer && this.resizeEl != this.el){
39268                 this.el.setSize(width, height);
39269             }
39270             var size = this.adjustForComponents(width, height);
39271             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39272             this.fireEvent('resize', this, size.width, size.height);
39273         }
39274     },
39275     
39276     /**
39277      * Returns this panel's title
39278      * @return {String} 
39279      */
39280     getTitle : function(){
39281         
39282         if (typeof(this.title) != 'object') {
39283             return this.title;
39284         }
39285         
39286         var t = '';
39287         for (var k in this.title) {
39288             if (!this.title.hasOwnProperty(k)) {
39289                 continue;
39290             }
39291             
39292             if (k.indexOf('-') >= 0) {
39293                 var s = k.split('-');
39294                 for (var i = 0; i<s.length; i++) {
39295                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39296                 }
39297             } else {
39298                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39299             }
39300         }
39301         return t;
39302     },
39303     
39304     /**
39305      * Set this panel's title
39306      * @param {String} title
39307      */
39308     setTitle : function(title){
39309         this.title = title;
39310         if(this.region){
39311             this.region.updatePanelTitle(this, title);
39312         }
39313     },
39314     
39315     /**
39316      * Returns true is this panel was configured to be closable
39317      * @return {Boolean} 
39318      */
39319     isClosable : function(){
39320         return this.closable;
39321     },
39322     
39323     beforeSlide : function(){
39324         this.el.clip();
39325         this.resizeEl.clip();
39326     },
39327     
39328     afterSlide : function(){
39329         this.el.unclip();
39330         this.resizeEl.unclip();
39331     },
39332     
39333     /**
39334      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39335      *   Will fail silently if the {@link #setUrl} method has not been called.
39336      *   This does not activate the panel, just updates its content.
39337      */
39338     refresh : function(){
39339         if(this.refreshDelegate){
39340            this.loaded = false;
39341            this.refreshDelegate();
39342         }
39343     },
39344     
39345     /**
39346      * Destroys this panel
39347      */
39348     destroy : function(){
39349         this.el.removeAllListeners();
39350         var tempEl = document.createElement("span");
39351         tempEl.appendChild(this.el.dom);
39352         tempEl.innerHTML = "";
39353         this.el.remove();
39354         this.el = null;
39355     },
39356     
39357     /**
39358      * form - if the content panel contains a form - this is a reference to it.
39359      * @type {Roo.form.Form}
39360      */
39361     form : false,
39362     /**
39363      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39364      *    This contains a reference to it.
39365      * @type {Roo.View}
39366      */
39367     view : false,
39368     
39369       /**
39370      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39371      * <pre><code>
39372
39373 layout.addxtype({
39374        xtype : 'Form',
39375        items: [ .... ]
39376    }
39377 );
39378
39379 </code></pre>
39380      * @param {Object} cfg Xtype definition of item to add.
39381      */
39382     
39383     
39384     getChildContainer: function () {
39385         return this.getEl();
39386     }
39387     
39388     
39389     /*
39390         var  ret = new Roo.factory(cfg);
39391         return ret;
39392         
39393         
39394         // add form..
39395         if (cfg.xtype.match(/^Form$/)) {
39396             
39397             var el;
39398             //if (this.footer) {
39399             //    el = this.footer.container.insertSibling(false, 'before');
39400             //} else {
39401                 el = this.el.createChild();
39402             //}
39403
39404             this.form = new  Roo.form.Form(cfg);
39405             
39406             
39407             if ( this.form.allItems.length) {
39408                 this.form.render(el.dom);
39409             }
39410             return this.form;
39411         }
39412         // should only have one of theses..
39413         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39414             // views.. should not be just added - used named prop 'view''
39415             
39416             cfg.el = this.el.appendChild(document.createElement("div"));
39417             // factory?
39418             
39419             var ret = new Roo.factory(cfg);
39420              
39421              ret.render && ret.render(false, ''); // render blank..
39422             this.view = ret;
39423             return ret;
39424         }
39425         return false;
39426     }
39427     \*/
39428 });
39429  
39430 /**
39431  * @class Roo.bootstrap.panel.Grid
39432  * @extends Roo.bootstrap.panel.Content
39433  * @constructor
39434  * Create a new GridPanel.
39435  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39436  * @param {Object} config A the config object
39437   
39438  */
39439
39440
39441
39442 Roo.bootstrap.panel.Grid = function(config)
39443 {
39444     
39445       
39446     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39447         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39448
39449     config.el = this.wrapper;
39450     //this.el = this.wrapper;
39451     
39452       if (config.container) {
39453         // ctor'ed from a Border/panel.grid
39454         
39455         
39456         this.wrapper.setStyle("overflow", "hidden");
39457         this.wrapper.addClass('roo-grid-container');
39458
39459     }
39460     
39461     
39462     if(config.toolbar){
39463         var tool_el = this.wrapper.createChild();    
39464         this.toolbar = Roo.factory(config.toolbar);
39465         var ti = [];
39466         if (config.toolbar.items) {
39467             ti = config.toolbar.items ;
39468             delete config.toolbar.items ;
39469         }
39470         
39471         var nitems = [];
39472         this.toolbar.render(tool_el);
39473         for(var i =0;i < ti.length;i++) {
39474           //  Roo.log(['add child', items[i]]);
39475             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39476         }
39477         this.toolbar.items = nitems;
39478         
39479         delete config.toolbar;
39480     }
39481     
39482     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39483     config.grid.scrollBody = true;;
39484     config.grid.monitorWindowResize = false; // turn off autosizing
39485     config.grid.autoHeight = false;
39486     config.grid.autoWidth = false;
39487     
39488     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39489     
39490     if (config.background) {
39491         // render grid on panel activation (if panel background)
39492         this.on('activate', function(gp) {
39493             if (!gp.grid.rendered) {
39494                 gp.grid.render(this.wrapper);
39495                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
39496             }
39497         });
39498             
39499     } else {
39500         this.grid.render(this.wrapper);
39501         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
39502
39503     }
39504     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39505     // ??? needed ??? config.el = this.wrapper;
39506     
39507     
39508     
39509   
39510     // xtype created footer. - not sure if will work as we normally have to render first..
39511     if (this.footer && !this.footer.el && this.footer.xtype) {
39512         
39513         var ctr = this.grid.getView().getFooterPanel(true);
39514         this.footer.dataSource = this.grid.dataSource;
39515         this.footer = Roo.factory(this.footer, Roo);
39516         this.footer.render(ctr);
39517         
39518     }
39519     
39520     
39521     
39522     
39523      
39524 };
39525
39526 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39527     getId : function(){
39528         return this.grid.id;
39529     },
39530     
39531     /**
39532      * Returns the grid for this panel
39533      * @return {Roo.bootstrap.Table} 
39534      */
39535     getGrid : function(){
39536         return this.grid;    
39537     },
39538     
39539     setSize : function(width, height){
39540         if(!this.ignoreResize(width, height)){
39541             var grid = this.grid;
39542             var size = this.adjustForComponents(width, height);
39543             var gridel = grid.getGridEl();
39544             gridel.setSize(size.width, size.height);
39545             /*
39546             var thd = grid.getGridEl().select('thead',true).first();
39547             var tbd = grid.getGridEl().select('tbody', true).first();
39548             if (tbd) {
39549                 tbd.setSize(width, height - thd.getHeight());
39550             }
39551             */
39552             grid.autoSize();
39553         }
39554     },
39555      
39556     
39557     
39558     beforeSlide : function(){
39559         this.grid.getView().scroller.clip();
39560     },
39561     
39562     afterSlide : function(){
39563         this.grid.getView().scroller.unclip();
39564     },
39565     
39566     destroy : function(){
39567         this.grid.destroy();
39568         delete this.grid;
39569         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
39570     }
39571 });
39572
39573 /**
39574  * @class Roo.bootstrap.panel.Nest
39575  * @extends Roo.bootstrap.panel.Content
39576  * @constructor
39577  * Create a new Panel, that can contain a layout.Border.
39578  * 
39579  * 
39580  * @param {Roo.BorderLayout} layout The layout for this panel
39581  * @param {String/Object} config A string to set only the title or a config object
39582  */
39583 Roo.bootstrap.panel.Nest = function(config)
39584 {
39585     // construct with only one argument..
39586     /* FIXME - implement nicer consturctors
39587     if (layout.layout) {
39588         config = layout;
39589         layout = config.layout;
39590         delete config.layout;
39591     }
39592     if (layout.xtype && !layout.getEl) {
39593         // then layout needs constructing..
39594         layout = Roo.factory(layout, Roo);
39595     }
39596     */
39597     
39598     config.el =  config.layout.getEl();
39599     
39600     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39601     
39602     config.layout.monitorWindowResize = false; // turn off autosizing
39603     this.layout = config.layout;
39604     this.layout.getEl().addClass("roo-layout-nested-layout");
39605     this.layout.parent = this;
39606     
39607     
39608     
39609     
39610 };
39611
39612 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39613
39614     setSize : function(width, height){
39615         if(!this.ignoreResize(width, height)){
39616             var size = this.adjustForComponents(width, height);
39617             var el = this.layout.getEl();
39618             if (size.height < 1) {
39619                 el.setWidth(size.width);   
39620             } else {
39621                 el.setSize(size.width, size.height);
39622             }
39623             var touch = el.dom.offsetWidth;
39624             this.layout.layout();
39625             // ie requires a double layout on the first pass
39626             if(Roo.isIE && !this.initialized){
39627                 this.initialized = true;
39628                 this.layout.layout();
39629             }
39630         }
39631     },
39632     
39633     // activate all subpanels if not currently active..
39634     
39635     setActiveState : function(active){
39636         this.active = active;
39637         this.setActiveClass(active);
39638         
39639         if(!active){
39640             this.fireEvent("deactivate", this);
39641             return;
39642         }
39643         
39644         this.fireEvent("activate", this);
39645         // not sure if this should happen before or after..
39646         if (!this.layout) {
39647             return; // should not happen..
39648         }
39649         var reg = false;
39650         for (var r in this.layout.regions) {
39651             reg = this.layout.getRegion(r);
39652             if (reg.getActivePanel()) {
39653                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
39654                 reg.setActivePanel(reg.getActivePanel());
39655                 continue;
39656             }
39657             if (!reg.panels.length) {
39658                 continue;
39659             }
39660             reg.showPanel(reg.getPanel(0));
39661         }
39662         
39663         
39664         
39665         
39666     },
39667     
39668     /**
39669      * Returns the nested BorderLayout for this panel
39670      * @return {Roo.BorderLayout} 
39671      */
39672     getLayout : function(){
39673         return this.layout;
39674     },
39675     
39676      /**
39677      * Adds a xtype elements to the layout of the nested panel
39678      * <pre><code>
39679
39680 panel.addxtype({
39681        xtype : 'ContentPanel',
39682        region: 'west',
39683        items: [ .... ]
39684    }
39685 );
39686
39687 panel.addxtype({
39688         xtype : 'NestedLayoutPanel',
39689         region: 'west',
39690         layout: {
39691            center: { },
39692            west: { }   
39693         },
39694         items : [ ... list of content panels or nested layout panels.. ]
39695    }
39696 );
39697 </code></pre>
39698      * @param {Object} cfg Xtype definition of item to add.
39699      */
39700     addxtype : function(cfg) {
39701         return this.layout.addxtype(cfg);
39702     
39703     }
39704 });/*
39705  * Based on:
39706  * Ext JS Library 1.1.1
39707  * Copyright(c) 2006-2007, Ext JS, LLC.
39708  *
39709  * Originally Released Under LGPL - original licence link has changed is not relivant.
39710  *
39711  * Fork - LGPL
39712  * <script type="text/javascript">
39713  */
39714 /**
39715  * @class Roo.TabPanel
39716  * @extends Roo.util.Observable
39717  * A lightweight tab container.
39718  * <br><br>
39719  * Usage:
39720  * <pre><code>
39721 // basic tabs 1, built from existing content
39722 var tabs = new Roo.TabPanel("tabs1");
39723 tabs.addTab("script", "View Script");
39724 tabs.addTab("markup", "View Markup");
39725 tabs.activate("script");
39726
39727 // more advanced tabs, built from javascript
39728 var jtabs = new Roo.TabPanel("jtabs");
39729 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39730
39731 // set up the UpdateManager
39732 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39733 var updater = tab2.getUpdateManager();
39734 updater.setDefaultUrl("ajax1.htm");
39735 tab2.on('activate', updater.refresh, updater, true);
39736
39737 // Use setUrl for Ajax loading
39738 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39739 tab3.setUrl("ajax2.htm", null, true);
39740
39741 // Disabled tab
39742 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39743 tab4.disable();
39744
39745 jtabs.activate("jtabs-1");
39746  * </code></pre>
39747  * @constructor
39748  * Create a new TabPanel.
39749  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39750  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39751  */
39752 Roo.bootstrap.panel.Tabs = function(config){
39753     /**
39754     * The container element for this TabPanel.
39755     * @type Roo.Element
39756     */
39757     this.el = Roo.get(config.el);
39758     delete config.el;
39759     if(config){
39760         if(typeof config == "boolean"){
39761             this.tabPosition = config ? "bottom" : "top";
39762         }else{
39763             Roo.apply(this, config);
39764         }
39765     }
39766     
39767     if(this.tabPosition == "bottom"){
39768         // if tabs are at the bottom = create the body first.
39769         this.bodyEl = Roo.get(this.createBody(this.el.dom));
39770         this.el.addClass("roo-tabs-bottom");
39771     }
39772     // next create the tabs holders
39773     
39774     if (this.tabPosition == "west"){
39775         
39776         var reg = this.region; // fake it..
39777         while (reg) {
39778             if (!reg.mgr.parent) {
39779                 break;
39780             }
39781             reg = reg.mgr.parent.region;
39782         }
39783         Roo.log("got nest?");
39784         Roo.log(reg);
39785         if (reg.mgr.getRegion('west')) {
39786             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
39787             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
39788             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39789             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39790             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39791         
39792             
39793         }
39794         
39795         
39796     } else {
39797      
39798         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
39799         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39800         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39801         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39802     }
39803     
39804     
39805     if(Roo.isIE){
39806         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
39807     }
39808     
39809     // finally - if tabs are at the top, then create the body last..
39810     if(this.tabPosition != "bottom"){
39811         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
39812          * @type Roo.Element
39813          */
39814         this.bodyEl = Roo.get(this.createBody(this.el.dom));
39815         this.el.addClass("roo-tabs-top");
39816     }
39817     this.items = [];
39818
39819     this.bodyEl.setStyle("position", "relative");
39820
39821     this.active = null;
39822     this.activateDelegate = this.activate.createDelegate(this);
39823
39824     this.addEvents({
39825         /**
39826          * @event tabchange
39827          * Fires when the active tab changes
39828          * @param {Roo.TabPanel} this
39829          * @param {Roo.TabPanelItem} activePanel The new active tab
39830          */
39831         "tabchange": true,
39832         /**
39833          * @event beforetabchange
39834          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
39835          * @param {Roo.TabPanel} this
39836          * @param {Object} e Set cancel to true on this object to cancel the tab change
39837          * @param {Roo.TabPanelItem} tab The tab being changed to
39838          */
39839         "beforetabchange" : true
39840     });
39841
39842     Roo.EventManager.onWindowResize(this.onResize, this);
39843     this.cpad = this.el.getPadding("lr");
39844     this.hiddenCount = 0;
39845
39846
39847     // toolbar on the tabbar support...
39848     if (this.toolbar) {
39849         alert("no toolbar support yet");
39850         this.toolbar  = false;
39851         /*
39852         var tcfg = this.toolbar;
39853         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
39854         this.toolbar = new Roo.Toolbar(tcfg);
39855         if (Roo.isSafari) {
39856             var tbl = tcfg.container.child('table', true);
39857             tbl.setAttribute('width', '100%');
39858         }
39859         */
39860         
39861     }
39862    
39863
39864
39865     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
39866 };
39867
39868 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
39869     /*
39870      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
39871      */
39872     tabPosition : "top",
39873     /*
39874      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
39875      */
39876     currentTabWidth : 0,
39877     /*
39878      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
39879      */
39880     minTabWidth : 40,
39881     /*
39882      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
39883      */
39884     maxTabWidth : 250,
39885     /*
39886      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
39887      */
39888     preferredTabWidth : 175,
39889     /*
39890      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
39891      */
39892     resizeTabs : false,
39893     /*
39894      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
39895      */
39896     monitorResize : true,
39897     /*
39898      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
39899      */
39900     toolbar : false,  // set by caller..
39901     
39902     region : false, /// set by caller
39903     
39904     disableTooltips : true, // not used yet...
39905
39906     /**
39907      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
39908      * @param {String} id The id of the div to use <b>or create</b>
39909      * @param {String} text The text for the tab
39910      * @param {String} content (optional) Content to put in the TabPanelItem body
39911      * @param {Boolean} closable (optional) True to create a close icon on the tab
39912      * @return {Roo.TabPanelItem} The created TabPanelItem
39913      */
39914     addTab : function(id, text, content, closable, tpl)
39915     {
39916         var item = new Roo.bootstrap.panel.TabItem({
39917             panel: this,
39918             id : id,
39919             text : text,
39920             closable : closable,
39921             tpl : tpl
39922         });
39923         this.addTabItem(item);
39924         if(content){
39925             item.setContent(content);
39926         }
39927         return item;
39928     },
39929
39930     /**
39931      * Returns the {@link Roo.TabPanelItem} with the specified id/index
39932      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
39933      * @return {Roo.TabPanelItem}
39934      */
39935     getTab : function(id){
39936         return this.items[id];
39937     },
39938
39939     /**
39940      * Hides the {@link Roo.TabPanelItem} with the specified id/index
39941      * @param {String/Number} id The id or index of the TabPanelItem to hide.
39942      */
39943     hideTab : function(id){
39944         var t = this.items[id];
39945         if(!t.isHidden()){
39946            t.setHidden(true);
39947            this.hiddenCount++;
39948            this.autoSizeTabs();
39949         }
39950     },
39951
39952     /**
39953      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
39954      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
39955      */
39956     unhideTab : function(id){
39957         var t = this.items[id];
39958         if(t.isHidden()){
39959            t.setHidden(false);
39960            this.hiddenCount--;
39961            this.autoSizeTabs();
39962         }
39963     },
39964
39965     /**
39966      * Adds an existing {@link Roo.TabPanelItem}.
39967      * @param {Roo.TabPanelItem} item The TabPanelItem to add
39968      */
39969     addTabItem : function(item)
39970     {
39971         this.items[item.id] = item;
39972         this.items.push(item);
39973         this.autoSizeTabs();
39974       //  if(this.resizeTabs){
39975     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
39976   //         this.autoSizeTabs();
39977 //        }else{
39978 //            item.autoSize();
39979        // }
39980     },
39981
39982     /**
39983      * Removes a {@link Roo.TabPanelItem}.
39984      * @param {String/Number} id The id or index of the TabPanelItem to remove.
39985      */
39986     removeTab : function(id){
39987         var items = this.items;
39988         var tab = items[id];
39989         if(!tab) { return; }
39990         var index = items.indexOf(tab);
39991         if(this.active == tab && items.length > 1){
39992             var newTab = this.getNextAvailable(index);
39993             if(newTab) {
39994                 newTab.activate();
39995             }
39996         }
39997         this.stripEl.dom.removeChild(tab.pnode.dom);
39998         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
39999             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40000         }
40001         items.splice(index, 1);
40002         delete this.items[tab.id];
40003         tab.fireEvent("close", tab);
40004         tab.purgeListeners();
40005         this.autoSizeTabs();
40006     },
40007
40008     getNextAvailable : function(start){
40009         var items = this.items;
40010         var index = start;
40011         // look for a next tab that will slide over to
40012         // replace the one being removed
40013         while(index < items.length){
40014             var item = items[++index];
40015             if(item && !item.isHidden()){
40016                 return item;
40017             }
40018         }
40019         // if one isn't found select the previous tab (on the left)
40020         index = start;
40021         while(index >= 0){
40022             var item = items[--index];
40023             if(item && !item.isHidden()){
40024                 return item;
40025             }
40026         }
40027         return null;
40028     },
40029
40030     /**
40031      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40032      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40033      */
40034     disableTab : function(id){
40035         var tab = this.items[id];
40036         if(tab && this.active != tab){
40037             tab.disable();
40038         }
40039     },
40040
40041     /**
40042      * Enables a {@link Roo.TabPanelItem} that is disabled.
40043      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40044      */
40045     enableTab : function(id){
40046         var tab = this.items[id];
40047         tab.enable();
40048     },
40049
40050     /**
40051      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40052      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40053      * @return {Roo.TabPanelItem} The TabPanelItem.
40054      */
40055     activate : function(id)
40056     {
40057         //Roo.log('activite:'  + id);
40058         
40059         var tab = this.items[id];
40060         if(!tab){
40061             return null;
40062         }
40063         if(tab == this.active || tab.disabled){
40064             return tab;
40065         }
40066         var e = {};
40067         this.fireEvent("beforetabchange", this, e, tab);
40068         if(e.cancel !== true && !tab.disabled){
40069             if(this.active){
40070                 this.active.hide();
40071             }
40072             this.active = this.items[id];
40073             this.active.show();
40074             this.fireEvent("tabchange", this, this.active);
40075         }
40076         return tab;
40077     },
40078
40079     /**
40080      * Gets the active {@link Roo.TabPanelItem}.
40081      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40082      */
40083     getActiveTab : function(){
40084         return this.active;
40085     },
40086
40087     /**
40088      * Updates the tab body element to fit the height of the container element
40089      * for overflow scrolling
40090      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40091      */
40092     syncHeight : function(targetHeight){
40093         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40094         var bm = this.bodyEl.getMargins();
40095         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40096         this.bodyEl.setHeight(newHeight);
40097         return newHeight;
40098     },
40099
40100     onResize : function(){
40101         if(this.monitorResize){
40102             this.autoSizeTabs();
40103         }
40104     },
40105
40106     /**
40107      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40108      */
40109     beginUpdate : function(){
40110         this.updating = true;
40111     },
40112
40113     /**
40114      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40115      */
40116     endUpdate : function(){
40117         this.updating = false;
40118         this.autoSizeTabs();
40119     },
40120
40121     /**
40122      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40123      */
40124     autoSizeTabs : function()
40125     {
40126         var count = this.items.length;
40127         var vcount = count - this.hiddenCount;
40128         
40129         if (vcount < 2) {
40130             this.stripEl.hide();
40131         } else {
40132             this.stripEl.show();
40133         }
40134         
40135         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40136             return;
40137         }
40138         
40139         
40140         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40141         var availWidth = Math.floor(w / vcount);
40142         var b = this.stripBody;
40143         if(b.getWidth() > w){
40144             var tabs = this.items;
40145             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40146             if(availWidth < this.minTabWidth){
40147                 /*if(!this.sleft){    // incomplete scrolling code
40148                     this.createScrollButtons();
40149                 }
40150                 this.showScroll();
40151                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40152             }
40153         }else{
40154             if(this.currentTabWidth < this.preferredTabWidth){
40155                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40156             }
40157         }
40158     },
40159
40160     /**
40161      * Returns the number of tabs in this TabPanel.
40162      * @return {Number}
40163      */
40164      getCount : function(){
40165          return this.items.length;
40166      },
40167
40168     /**
40169      * Resizes all the tabs to the passed width
40170      * @param {Number} The new width
40171      */
40172     setTabWidth : function(width){
40173         this.currentTabWidth = width;
40174         for(var i = 0, len = this.items.length; i < len; i++) {
40175                 if(!this.items[i].isHidden()) {
40176                 this.items[i].setWidth(width);
40177             }
40178         }
40179     },
40180
40181     /**
40182      * Destroys this TabPanel
40183      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40184      */
40185     destroy : function(removeEl){
40186         Roo.EventManager.removeResizeListener(this.onResize, this);
40187         for(var i = 0, len = this.items.length; i < len; i++){
40188             this.items[i].purgeListeners();
40189         }
40190         if(removeEl === true){
40191             this.el.update("");
40192             this.el.remove();
40193         }
40194     },
40195     
40196     createStrip : function(container)
40197     {
40198         var strip = document.createElement("nav");
40199         strip.className = Roo.bootstrap.version == 4 ?
40200             "navbar-light bg-light" : 
40201             "navbar navbar-default"; //"x-tabs-wrap";
40202         container.appendChild(strip);
40203         return strip;
40204     },
40205     
40206     createStripList : function(strip)
40207     {
40208         // div wrapper for retard IE
40209         // returns the "tr" element.
40210         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40211         //'<div class="x-tabs-strip-wrap">'+
40212           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40213           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40214         return strip.firstChild; //.firstChild.firstChild.firstChild;
40215     },
40216     createBody : function(container)
40217     {
40218         var body = document.createElement("div");
40219         Roo.id(body, "tab-body");
40220         //Roo.fly(body).addClass("x-tabs-body");
40221         Roo.fly(body).addClass("tab-content");
40222         container.appendChild(body);
40223         return body;
40224     },
40225     createItemBody :function(bodyEl, id){
40226         var body = Roo.getDom(id);
40227         if(!body){
40228             body = document.createElement("div");
40229             body.id = id;
40230         }
40231         //Roo.fly(body).addClass("x-tabs-item-body");
40232         Roo.fly(body).addClass("tab-pane");
40233          bodyEl.insertBefore(body, bodyEl.firstChild);
40234         return body;
40235     },
40236     /** @private */
40237     createStripElements :  function(stripEl, text, closable, tpl)
40238     {
40239         var td = document.createElement("li"); // was td..
40240         td.className = 'nav-item';
40241         
40242         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40243         
40244         
40245         stripEl.appendChild(td);
40246         /*if(closable){
40247             td.className = "x-tabs-closable";
40248             if(!this.closeTpl){
40249                 this.closeTpl = new Roo.Template(
40250                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40251                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40252                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40253                 );
40254             }
40255             var el = this.closeTpl.overwrite(td, {"text": text});
40256             var close = el.getElementsByTagName("div")[0];
40257             var inner = el.getElementsByTagName("em")[0];
40258             return {"el": el, "close": close, "inner": inner};
40259         } else {
40260         */
40261         // not sure what this is..
40262 //            if(!this.tabTpl){
40263                 //this.tabTpl = new Roo.Template(
40264                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40265                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40266                 //);
40267 //                this.tabTpl = new Roo.Template(
40268 //                   '<a href="#">' +
40269 //                   '<span unselectable="on"' +
40270 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40271 //                            ' >{text}</span></a>'
40272 //                );
40273 //                
40274 //            }
40275
40276
40277             var template = tpl || this.tabTpl || false;
40278             
40279             if(!template){
40280                 template =  new Roo.Template(
40281                         Roo.bootstrap.version == 4 ? 
40282                             (
40283                                 '<a class="nav-link" href="#" unselectable="on"' +
40284                                      (this.disableTooltips ? '' : ' title="{text}"') +
40285                                      ' >{text}</a>'
40286                             ) : (
40287                                 '<a class="nav-link" href="#">' +
40288                                 '<span unselectable="on"' +
40289                                          (this.disableTooltips ? '' : ' title="{text}"') +
40290                                     ' >{text}</span></a>'
40291                             )
40292                 );
40293             }
40294             
40295             switch (typeof(template)) {
40296                 case 'object' :
40297                     break;
40298                 case 'string' :
40299                     template = new Roo.Template(template);
40300                     break;
40301                 default :
40302                     break;
40303             }
40304             
40305             var el = template.overwrite(td, {"text": text});
40306             
40307             var inner = el.getElementsByTagName("span")[0];
40308             
40309             return {"el": el, "inner": inner};
40310             
40311     }
40312         
40313     
40314 });
40315
40316 /**
40317  * @class Roo.TabPanelItem
40318  * @extends Roo.util.Observable
40319  * Represents an individual item (tab plus body) in a TabPanel.
40320  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40321  * @param {String} id The id of this TabPanelItem
40322  * @param {String} text The text for the tab of this TabPanelItem
40323  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40324  */
40325 Roo.bootstrap.panel.TabItem = function(config){
40326     /**
40327      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40328      * @type Roo.TabPanel
40329      */
40330     this.tabPanel = config.panel;
40331     /**
40332      * The id for this TabPanelItem
40333      * @type String
40334      */
40335     this.id = config.id;
40336     /** @private */
40337     this.disabled = false;
40338     /** @private */
40339     this.text = config.text;
40340     /** @private */
40341     this.loaded = false;
40342     this.closable = config.closable;
40343
40344     /**
40345      * The body element for this TabPanelItem.
40346      * @type Roo.Element
40347      */
40348     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40349     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40350     this.bodyEl.setStyle("display", "block");
40351     this.bodyEl.setStyle("zoom", "1");
40352     //this.hideAction();
40353
40354     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40355     /** @private */
40356     this.el = Roo.get(els.el);
40357     this.inner = Roo.get(els.inner, true);
40358      this.textEl = Roo.bootstrap.version == 4 ?
40359         this.el : Roo.get(this.el.dom.firstChild, true);
40360
40361     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40362     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40363
40364     
40365 //    this.el.on("mousedown", this.onTabMouseDown, this);
40366     this.el.on("click", this.onTabClick, this);
40367     /** @private */
40368     if(config.closable){
40369         var c = Roo.get(els.close, true);
40370         c.dom.title = this.closeText;
40371         c.addClassOnOver("close-over");
40372         c.on("click", this.closeClick, this);
40373      }
40374
40375     this.addEvents({
40376          /**
40377          * @event activate
40378          * Fires when this tab becomes the active tab.
40379          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40380          * @param {Roo.TabPanelItem} this
40381          */
40382         "activate": true,
40383         /**
40384          * @event beforeclose
40385          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40386          * @param {Roo.TabPanelItem} this
40387          * @param {Object} e Set cancel to true on this object to cancel the close.
40388          */
40389         "beforeclose": true,
40390         /**
40391          * @event close
40392          * Fires when this tab is closed.
40393          * @param {Roo.TabPanelItem} this
40394          */
40395          "close": true,
40396         /**
40397          * @event deactivate
40398          * Fires when this tab is no longer the active tab.
40399          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40400          * @param {Roo.TabPanelItem} this
40401          */
40402          "deactivate" : true
40403     });
40404     this.hidden = false;
40405
40406     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40407 };
40408
40409 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40410            {
40411     purgeListeners : function(){
40412        Roo.util.Observable.prototype.purgeListeners.call(this);
40413        this.el.removeAllListeners();
40414     },
40415     /**
40416      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40417      */
40418     show : function(){
40419         this.status_node.addClass("active");
40420         this.showAction();
40421         if(Roo.isOpera){
40422             this.tabPanel.stripWrap.repaint();
40423         }
40424         this.fireEvent("activate", this.tabPanel, this);
40425     },
40426
40427     /**
40428      * Returns true if this tab is the active tab.
40429      * @return {Boolean}
40430      */
40431     isActive : function(){
40432         return this.tabPanel.getActiveTab() == this;
40433     },
40434
40435     /**
40436      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40437      */
40438     hide : function(){
40439         this.status_node.removeClass("active");
40440         this.hideAction();
40441         this.fireEvent("deactivate", this.tabPanel, this);
40442     },
40443
40444     hideAction : function(){
40445         this.bodyEl.hide();
40446         this.bodyEl.setStyle("position", "absolute");
40447         this.bodyEl.setLeft("-20000px");
40448         this.bodyEl.setTop("-20000px");
40449     },
40450
40451     showAction : function(){
40452         this.bodyEl.setStyle("position", "relative");
40453         this.bodyEl.setTop("");
40454         this.bodyEl.setLeft("");
40455         this.bodyEl.show();
40456     },
40457
40458     /**
40459      * Set the tooltip for the tab.
40460      * @param {String} tooltip The tab's tooltip
40461      */
40462     setTooltip : function(text){
40463         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40464             this.textEl.dom.qtip = text;
40465             this.textEl.dom.removeAttribute('title');
40466         }else{
40467             this.textEl.dom.title = text;
40468         }
40469     },
40470
40471     onTabClick : function(e){
40472         e.preventDefault();
40473         this.tabPanel.activate(this.id);
40474     },
40475
40476     onTabMouseDown : function(e){
40477         e.preventDefault();
40478         this.tabPanel.activate(this.id);
40479     },
40480 /*
40481     getWidth : function(){
40482         return this.inner.getWidth();
40483     },
40484
40485     setWidth : function(width){
40486         var iwidth = width - this.linode.getPadding("lr");
40487         this.inner.setWidth(iwidth);
40488         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40489         this.linode.setWidth(width);
40490     },
40491 */
40492     /**
40493      * Show or hide the tab
40494      * @param {Boolean} hidden True to hide or false to show.
40495      */
40496     setHidden : function(hidden){
40497         this.hidden = hidden;
40498         this.linode.setStyle("display", hidden ? "none" : "");
40499     },
40500
40501     /**
40502      * Returns true if this tab is "hidden"
40503      * @return {Boolean}
40504      */
40505     isHidden : function(){
40506         return this.hidden;
40507     },
40508
40509     /**
40510      * Returns the text for this tab
40511      * @return {String}
40512      */
40513     getText : function(){
40514         return this.text;
40515     },
40516     /*
40517     autoSize : function(){
40518         //this.el.beginMeasure();
40519         this.textEl.setWidth(1);
40520         /*
40521          *  #2804 [new] Tabs in Roojs
40522          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40523          */
40524         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40525         //this.el.endMeasure();
40526     //},
40527
40528     /**
40529      * Sets the text for the tab (Note: this also sets the tooltip text)
40530      * @param {String} text The tab's text and tooltip
40531      */
40532     setText : function(text){
40533         this.text = text;
40534         this.textEl.update(text);
40535         this.setTooltip(text);
40536         //if(!this.tabPanel.resizeTabs){
40537         //    this.autoSize();
40538         //}
40539     },
40540     /**
40541      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40542      */
40543     activate : function(){
40544         this.tabPanel.activate(this.id);
40545     },
40546
40547     /**
40548      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40549      */
40550     disable : function(){
40551         if(this.tabPanel.active != this){
40552             this.disabled = true;
40553             this.status_node.addClass("disabled");
40554         }
40555     },
40556
40557     /**
40558      * Enables this TabPanelItem if it was previously disabled.
40559      */
40560     enable : function(){
40561         this.disabled = false;
40562         this.status_node.removeClass("disabled");
40563     },
40564
40565     /**
40566      * Sets the content for this TabPanelItem.
40567      * @param {String} content The content
40568      * @param {Boolean} loadScripts true to look for and load scripts
40569      */
40570     setContent : function(content, loadScripts){
40571         this.bodyEl.update(content, loadScripts);
40572     },
40573
40574     /**
40575      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40576      * @return {Roo.UpdateManager} The UpdateManager
40577      */
40578     getUpdateManager : function(){
40579         return this.bodyEl.getUpdateManager();
40580     },
40581
40582     /**
40583      * Set a URL to be used to load the content for this TabPanelItem.
40584      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40585      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
40586      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
40587      * @return {Roo.UpdateManager} The UpdateManager
40588      */
40589     setUrl : function(url, params, loadOnce){
40590         if(this.refreshDelegate){
40591             this.un('activate', this.refreshDelegate);
40592         }
40593         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40594         this.on("activate", this.refreshDelegate);
40595         return this.bodyEl.getUpdateManager();
40596     },
40597
40598     /** @private */
40599     _handleRefresh : function(url, params, loadOnce){
40600         if(!loadOnce || !this.loaded){
40601             var updater = this.bodyEl.getUpdateManager();
40602             updater.update(url, params, this._setLoaded.createDelegate(this));
40603         }
40604     },
40605
40606     /**
40607      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
40608      *   Will fail silently if the setUrl method has not been called.
40609      *   This does not activate the panel, just updates its content.
40610      */
40611     refresh : function(){
40612         if(this.refreshDelegate){
40613            this.loaded = false;
40614            this.refreshDelegate();
40615         }
40616     },
40617
40618     /** @private */
40619     _setLoaded : function(){
40620         this.loaded = true;
40621     },
40622
40623     /** @private */
40624     closeClick : function(e){
40625         var o = {};
40626         e.stopEvent();
40627         this.fireEvent("beforeclose", this, o);
40628         if(o.cancel !== true){
40629             this.tabPanel.removeTab(this.id);
40630         }
40631     },
40632     /**
40633      * The text displayed in the tooltip for the close icon.
40634      * @type String
40635      */
40636     closeText : "Close this tab"
40637 });
40638 /**
40639 *    This script refer to:
40640 *    Title: International Telephone Input
40641 *    Author: Jack O'Connor
40642 *    Code version:  v12.1.12
40643 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40644 **/
40645
40646 Roo.bootstrap.PhoneInputData = function() {
40647     var d = [
40648       [
40649         "Afghanistan (‫افغانستان‬‎)",
40650         "af",
40651         "93"
40652       ],
40653       [
40654         "Albania (Shqipëri)",
40655         "al",
40656         "355"
40657       ],
40658       [
40659         "Algeria (‫الجزائر‬‎)",
40660         "dz",
40661         "213"
40662       ],
40663       [
40664         "American Samoa",
40665         "as",
40666         "1684"
40667       ],
40668       [
40669         "Andorra",
40670         "ad",
40671         "376"
40672       ],
40673       [
40674         "Angola",
40675         "ao",
40676         "244"
40677       ],
40678       [
40679         "Anguilla",
40680         "ai",
40681         "1264"
40682       ],
40683       [
40684         "Antigua and Barbuda",
40685         "ag",
40686         "1268"
40687       ],
40688       [
40689         "Argentina",
40690         "ar",
40691         "54"
40692       ],
40693       [
40694         "Armenia (Հայաստան)",
40695         "am",
40696         "374"
40697       ],
40698       [
40699         "Aruba",
40700         "aw",
40701         "297"
40702       ],
40703       [
40704         "Australia",
40705         "au",
40706         "61",
40707         0
40708       ],
40709       [
40710         "Austria (Österreich)",
40711         "at",
40712         "43"
40713       ],
40714       [
40715         "Azerbaijan (Azərbaycan)",
40716         "az",
40717         "994"
40718       ],
40719       [
40720         "Bahamas",
40721         "bs",
40722         "1242"
40723       ],
40724       [
40725         "Bahrain (‫البحرين‬‎)",
40726         "bh",
40727         "973"
40728       ],
40729       [
40730         "Bangladesh (বাংলাদেশ)",
40731         "bd",
40732         "880"
40733       ],
40734       [
40735         "Barbados",
40736         "bb",
40737         "1246"
40738       ],
40739       [
40740         "Belarus (Беларусь)",
40741         "by",
40742         "375"
40743       ],
40744       [
40745         "Belgium (België)",
40746         "be",
40747         "32"
40748       ],
40749       [
40750         "Belize",
40751         "bz",
40752         "501"
40753       ],
40754       [
40755         "Benin (Bénin)",
40756         "bj",
40757         "229"
40758       ],
40759       [
40760         "Bermuda",
40761         "bm",
40762         "1441"
40763       ],
40764       [
40765         "Bhutan (འབྲུག)",
40766         "bt",
40767         "975"
40768       ],
40769       [
40770         "Bolivia",
40771         "bo",
40772         "591"
40773       ],
40774       [
40775         "Bosnia and Herzegovina (Босна и Херцеговина)",
40776         "ba",
40777         "387"
40778       ],
40779       [
40780         "Botswana",
40781         "bw",
40782         "267"
40783       ],
40784       [
40785         "Brazil (Brasil)",
40786         "br",
40787         "55"
40788       ],
40789       [
40790         "British Indian Ocean Territory",
40791         "io",
40792         "246"
40793       ],
40794       [
40795         "British Virgin Islands",
40796         "vg",
40797         "1284"
40798       ],
40799       [
40800         "Brunei",
40801         "bn",
40802         "673"
40803       ],
40804       [
40805         "Bulgaria (България)",
40806         "bg",
40807         "359"
40808       ],
40809       [
40810         "Burkina Faso",
40811         "bf",
40812         "226"
40813       ],
40814       [
40815         "Burundi (Uburundi)",
40816         "bi",
40817         "257"
40818       ],
40819       [
40820         "Cambodia (កម្ពុជា)",
40821         "kh",
40822         "855"
40823       ],
40824       [
40825         "Cameroon (Cameroun)",
40826         "cm",
40827         "237"
40828       ],
40829       [
40830         "Canada",
40831         "ca",
40832         "1",
40833         1,
40834         ["204", "226", "236", "249", "250", "289", "306", "343", "365", "387", "403", "416", "418", "431", "437", "438", "450", "506", "514", "519", "548", "579", "581", "587", "604", "613", "639", "647", "672", "705", "709", "742", "778", "780", "782", "807", "819", "825", "867", "873", "902", "905"]
40835       ],
40836       [
40837         "Cape Verde (Kabu Verdi)",
40838         "cv",
40839         "238"
40840       ],
40841       [
40842         "Caribbean Netherlands",
40843         "bq",
40844         "599",
40845         1
40846       ],
40847       [
40848         "Cayman Islands",
40849         "ky",
40850         "1345"
40851       ],
40852       [
40853         "Central African Republic (République centrafricaine)",
40854         "cf",
40855         "236"
40856       ],
40857       [
40858         "Chad (Tchad)",
40859         "td",
40860         "235"
40861       ],
40862       [
40863         "Chile",
40864         "cl",
40865         "56"
40866       ],
40867       [
40868         "China (中国)",
40869         "cn",
40870         "86"
40871       ],
40872       [
40873         "Christmas Island",
40874         "cx",
40875         "61",
40876         2
40877       ],
40878       [
40879         "Cocos (Keeling) Islands",
40880         "cc",
40881         "61",
40882         1
40883       ],
40884       [
40885         "Colombia",
40886         "co",
40887         "57"
40888       ],
40889       [
40890         "Comoros (‫جزر القمر‬‎)",
40891         "km",
40892         "269"
40893       ],
40894       [
40895         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
40896         "cd",
40897         "243"
40898       ],
40899       [
40900         "Congo (Republic) (Congo-Brazzaville)",
40901         "cg",
40902         "242"
40903       ],
40904       [
40905         "Cook Islands",
40906         "ck",
40907         "682"
40908       ],
40909       [
40910         "Costa Rica",
40911         "cr",
40912         "506"
40913       ],
40914       [
40915         "Côte d’Ivoire",
40916         "ci",
40917         "225"
40918       ],
40919       [
40920         "Croatia (Hrvatska)",
40921         "hr",
40922         "385"
40923       ],
40924       [
40925         "Cuba",
40926         "cu",
40927         "53"
40928       ],
40929       [
40930         "Curaçao",
40931         "cw",
40932         "599",
40933         0
40934       ],
40935       [
40936         "Cyprus (Κύπρος)",
40937         "cy",
40938         "357"
40939       ],
40940       [
40941         "Czech Republic (Česká republika)",
40942         "cz",
40943         "420"
40944       ],
40945       [
40946         "Denmark (Danmark)",
40947         "dk",
40948         "45"
40949       ],
40950       [
40951         "Djibouti",
40952         "dj",
40953         "253"
40954       ],
40955       [
40956         "Dominica",
40957         "dm",
40958         "1767"
40959       ],
40960       [
40961         "Dominican Republic (República Dominicana)",
40962         "do",
40963         "1",
40964         2,
40965         ["809", "829", "849"]
40966       ],
40967       [
40968         "Ecuador",
40969         "ec",
40970         "593"
40971       ],
40972       [
40973         "Egypt (‫مصر‬‎)",
40974         "eg",
40975         "20"
40976       ],
40977       [
40978         "El Salvador",
40979         "sv",
40980         "503"
40981       ],
40982       [
40983         "Equatorial Guinea (Guinea Ecuatorial)",
40984         "gq",
40985         "240"
40986       ],
40987       [
40988         "Eritrea",
40989         "er",
40990         "291"
40991       ],
40992       [
40993         "Estonia (Eesti)",
40994         "ee",
40995         "372"
40996       ],
40997       [
40998         "Ethiopia",
40999         "et",
41000         "251"
41001       ],
41002       [
41003         "Falkland Islands (Islas Malvinas)",
41004         "fk",
41005         "500"
41006       ],
41007       [
41008         "Faroe Islands (Føroyar)",
41009         "fo",
41010         "298"
41011       ],
41012       [
41013         "Fiji",
41014         "fj",
41015         "679"
41016       ],
41017       [
41018         "Finland (Suomi)",
41019         "fi",
41020         "358",
41021         0
41022       ],
41023       [
41024         "France",
41025         "fr",
41026         "33"
41027       ],
41028       [
41029         "French Guiana (Guyane française)",
41030         "gf",
41031         "594"
41032       ],
41033       [
41034         "French Polynesia (Polynésie française)",
41035         "pf",
41036         "689"
41037       ],
41038       [
41039         "Gabon",
41040         "ga",
41041         "241"
41042       ],
41043       [
41044         "Gambia",
41045         "gm",
41046         "220"
41047       ],
41048       [
41049         "Georgia (საქართველო)",
41050         "ge",
41051         "995"
41052       ],
41053       [
41054         "Germany (Deutschland)",
41055         "de",
41056         "49"
41057       ],
41058       [
41059         "Ghana (Gaana)",
41060         "gh",
41061         "233"
41062       ],
41063       [
41064         "Gibraltar",
41065         "gi",
41066         "350"
41067       ],
41068       [
41069         "Greece (Ελλάδα)",
41070         "gr",
41071         "30"
41072       ],
41073       [
41074         "Greenland (Kalaallit Nunaat)",
41075         "gl",
41076         "299"
41077       ],
41078       [
41079         "Grenada",
41080         "gd",
41081         "1473"
41082       ],
41083       [
41084         "Guadeloupe",
41085         "gp",
41086         "590",
41087         0
41088       ],
41089       [
41090         "Guam",
41091         "gu",
41092         "1671"
41093       ],
41094       [
41095         "Guatemala",
41096         "gt",
41097         "502"
41098       ],
41099       [
41100         "Guernsey",
41101         "gg",
41102         "44",
41103         1
41104       ],
41105       [
41106         "Guinea (Guinée)",
41107         "gn",
41108         "224"
41109       ],
41110       [
41111         "Guinea-Bissau (Guiné Bissau)",
41112         "gw",
41113         "245"
41114       ],
41115       [
41116         "Guyana",
41117         "gy",
41118         "592"
41119       ],
41120       [
41121         "Haiti",
41122         "ht",
41123         "509"
41124       ],
41125       [
41126         "Honduras",
41127         "hn",
41128         "504"
41129       ],
41130       [
41131         "Hong Kong (香港)",
41132         "hk",
41133         "852"
41134       ],
41135       [
41136         "Hungary (Magyarország)",
41137         "hu",
41138         "36"
41139       ],
41140       [
41141         "Iceland (Ísland)",
41142         "is",
41143         "354"
41144       ],
41145       [
41146         "India (भारत)",
41147         "in",
41148         "91"
41149       ],
41150       [
41151         "Indonesia",
41152         "id",
41153         "62"
41154       ],
41155       [
41156         "Iran (‫ایران‬‎)",
41157         "ir",
41158         "98"
41159       ],
41160       [
41161         "Iraq (‫العراق‬‎)",
41162         "iq",
41163         "964"
41164       ],
41165       [
41166         "Ireland",
41167         "ie",
41168         "353"
41169       ],
41170       [
41171         "Isle of Man",
41172         "im",
41173         "44",
41174         2
41175       ],
41176       [
41177         "Israel (‫ישראל‬‎)",
41178         "il",
41179         "972"
41180       ],
41181       [
41182         "Italy (Italia)",
41183         "it",
41184         "39",
41185         0
41186       ],
41187       [
41188         "Jamaica",
41189         "jm",
41190         "1876"
41191       ],
41192       [
41193         "Japan (日本)",
41194         "jp",
41195         "81"
41196       ],
41197       [
41198         "Jersey",
41199         "je",
41200         "44",
41201         3
41202       ],
41203       [
41204         "Jordan (‫الأردن‬‎)",
41205         "jo",
41206         "962"
41207       ],
41208       [
41209         "Kazakhstan (Казахстан)",
41210         "kz",
41211         "7",
41212         1
41213       ],
41214       [
41215         "Kenya",
41216         "ke",
41217         "254"
41218       ],
41219       [
41220         "Kiribati",
41221         "ki",
41222         "686"
41223       ],
41224       [
41225         "Kosovo",
41226         "xk",
41227         "383"
41228       ],
41229       [
41230         "Kuwait (‫الكويت‬‎)",
41231         "kw",
41232         "965"
41233       ],
41234       [
41235         "Kyrgyzstan (Кыргызстан)",
41236         "kg",
41237         "996"
41238       ],
41239       [
41240         "Laos (ລາວ)",
41241         "la",
41242         "856"
41243       ],
41244       [
41245         "Latvia (Latvija)",
41246         "lv",
41247         "371"
41248       ],
41249       [
41250         "Lebanon (‫لبنان‬‎)",
41251         "lb",
41252         "961"
41253       ],
41254       [
41255         "Lesotho",
41256         "ls",
41257         "266"
41258       ],
41259       [
41260         "Liberia",
41261         "lr",
41262         "231"
41263       ],
41264       [
41265         "Libya (‫ليبيا‬‎)",
41266         "ly",
41267         "218"
41268       ],
41269       [
41270         "Liechtenstein",
41271         "li",
41272         "423"
41273       ],
41274       [
41275         "Lithuania (Lietuva)",
41276         "lt",
41277         "370"
41278       ],
41279       [
41280         "Luxembourg",
41281         "lu",
41282         "352"
41283       ],
41284       [
41285         "Macau (澳門)",
41286         "mo",
41287         "853"
41288       ],
41289       [
41290         "Macedonia (FYROM) (Македонија)",
41291         "mk",
41292         "389"
41293       ],
41294       [
41295         "Madagascar (Madagasikara)",
41296         "mg",
41297         "261"
41298       ],
41299       [
41300         "Malawi",
41301         "mw",
41302         "265"
41303       ],
41304       [
41305         "Malaysia",
41306         "my",
41307         "60"
41308       ],
41309       [
41310         "Maldives",
41311         "mv",
41312         "960"
41313       ],
41314       [
41315         "Mali",
41316         "ml",
41317         "223"
41318       ],
41319       [
41320         "Malta",
41321         "mt",
41322         "356"
41323       ],
41324       [
41325         "Marshall Islands",
41326         "mh",
41327         "692"
41328       ],
41329       [
41330         "Martinique",
41331         "mq",
41332         "596"
41333       ],
41334       [
41335         "Mauritania (‫موريتانيا‬‎)",
41336         "mr",
41337         "222"
41338       ],
41339       [
41340         "Mauritius (Moris)",
41341         "mu",
41342         "230"
41343       ],
41344       [
41345         "Mayotte",
41346         "yt",
41347         "262",
41348         1
41349       ],
41350       [
41351         "Mexico (México)",
41352         "mx",
41353         "52"
41354       ],
41355       [
41356         "Micronesia",
41357         "fm",
41358         "691"
41359       ],
41360       [
41361         "Moldova (Republica Moldova)",
41362         "md",
41363         "373"
41364       ],
41365       [
41366         "Monaco",
41367         "mc",
41368         "377"
41369       ],
41370       [
41371         "Mongolia (Монгол)",
41372         "mn",
41373         "976"
41374       ],
41375       [
41376         "Montenegro (Crna Gora)",
41377         "me",
41378         "382"
41379       ],
41380       [
41381         "Montserrat",
41382         "ms",
41383         "1664"
41384       ],
41385       [
41386         "Morocco (‫المغرب‬‎)",
41387         "ma",
41388         "212",
41389         0
41390       ],
41391       [
41392         "Mozambique (Moçambique)",
41393         "mz",
41394         "258"
41395       ],
41396       [
41397         "Myanmar (Burma) (မြန်မာ)",
41398         "mm",
41399         "95"
41400       ],
41401       [
41402         "Namibia (Namibië)",
41403         "na",
41404         "264"
41405       ],
41406       [
41407         "Nauru",
41408         "nr",
41409         "674"
41410       ],
41411       [
41412         "Nepal (नेपाल)",
41413         "np",
41414         "977"
41415       ],
41416       [
41417         "Netherlands (Nederland)",
41418         "nl",
41419         "31"
41420       ],
41421       [
41422         "New Caledonia (Nouvelle-Calédonie)",
41423         "nc",
41424         "687"
41425       ],
41426       [
41427         "New Zealand",
41428         "nz",
41429         "64"
41430       ],
41431       [
41432         "Nicaragua",
41433         "ni",
41434         "505"
41435       ],
41436       [
41437         "Niger (Nijar)",
41438         "ne",
41439         "227"
41440       ],
41441       [
41442         "Nigeria",
41443         "ng",
41444         "234"
41445       ],
41446       [
41447         "Niue",
41448         "nu",
41449         "683"
41450       ],
41451       [
41452         "Norfolk Island",
41453         "nf",
41454         "672"
41455       ],
41456       [
41457         "North Korea (조선 민주주의 인민 공화국)",
41458         "kp",
41459         "850"
41460       ],
41461       [
41462         "Northern Mariana Islands",
41463         "mp",
41464         "1670"
41465       ],
41466       [
41467         "Norway (Norge)",
41468         "no",
41469         "47",
41470         0
41471       ],
41472       [
41473         "Oman (‫عُمان‬‎)",
41474         "om",
41475         "968"
41476       ],
41477       [
41478         "Pakistan (‫پاکستان‬‎)",
41479         "pk",
41480         "92"
41481       ],
41482       [
41483         "Palau",
41484         "pw",
41485         "680"
41486       ],
41487       [
41488         "Palestine (‫فلسطين‬‎)",
41489         "ps",
41490         "970"
41491       ],
41492       [
41493         "Panama (Panamá)",
41494         "pa",
41495         "507"
41496       ],
41497       [
41498         "Papua New Guinea",
41499         "pg",
41500         "675"
41501       ],
41502       [
41503         "Paraguay",
41504         "py",
41505         "595"
41506       ],
41507       [
41508         "Peru (Perú)",
41509         "pe",
41510         "51"
41511       ],
41512       [
41513         "Philippines",
41514         "ph",
41515         "63"
41516       ],
41517       [
41518         "Poland (Polska)",
41519         "pl",
41520         "48"
41521       ],
41522       [
41523         "Portugal",
41524         "pt",
41525         "351"
41526       ],
41527       [
41528         "Puerto Rico",
41529         "pr",
41530         "1",
41531         3,
41532         ["787", "939"]
41533       ],
41534       [
41535         "Qatar (‫قطر‬‎)",
41536         "qa",
41537         "974"
41538       ],
41539       [
41540         "Réunion (La Réunion)",
41541         "re",
41542         "262",
41543         0
41544       ],
41545       [
41546         "Romania (România)",
41547         "ro",
41548         "40"
41549       ],
41550       [
41551         "Russia (Россия)",
41552         "ru",
41553         "7",
41554         0
41555       ],
41556       [
41557         "Rwanda",
41558         "rw",
41559         "250"
41560       ],
41561       [
41562         "Saint Barthélemy",
41563         "bl",
41564         "590",
41565         1
41566       ],
41567       [
41568         "Saint Helena",
41569         "sh",
41570         "290"
41571       ],
41572       [
41573         "Saint Kitts and Nevis",
41574         "kn",
41575         "1869"
41576       ],
41577       [
41578         "Saint Lucia",
41579         "lc",
41580         "1758"
41581       ],
41582       [
41583         "Saint Martin (Saint-Martin (partie française))",
41584         "mf",
41585         "590",
41586         2
41587       ],
41588       [
41589         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41590         "pm",
41591         "508"
41592       ],
41593       [
41594         "Saint Vincent and the Grenadines",
41595         "vc",
41596         "1784"
41597       ],
41598       [
41599         "Samoa",
41600         "ws",
41601         "685"
41602       ],
41603       [
41604         "San Marino",
41605         "sm",
41606         "378"
41607       ],
41608       [
41609         "São Tomé and Príncipe (São Tomé e Príncipe)",
41610         "st",
41611         "239"
41612       ],
41613       [
41614         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
41615         "sa",
41616         "966"
41617       ],
41618       [
41619         "Senegal (Sénégal)",
41620         "sn",
41621         "221"
41622       ],
41623       [
41624         "Serbia (Србија)",
41625         "rs",
41626         "381"
41627       ],
41628       [
41629         "Seychelles",
41630         "sc",
41631         "248"
41632       ],
41633       [
41634         "Sierra Leone",
41635         "sl",
41636         "232"
41637       ],
41638       [
41639         "Singapore",
41640         "sg",
41641         "65"
41642       ],
41643       [
41644         "Sint Maarten",
41645         "sx",
41646         "1721"
41647       ],
41648       [
41649         "Slovakia (Slovensko)",
41650         "sk",
41651         "421"
41652       ],
41653       [
41654         "Slovenia (Slovenija)",
41655         "si",
41656         "386"
41657       ],
41658       [
41659         "Solomon Islands",
41660         "sb",
41661         "677"
41662       ],
41663       [
41664         "Somalia (Soomaaliya)",
41665         "so",
41666         "252"
41667       ],
41668       [
41669         "South Africa",
41670         "za",
41671         "27"
41672       ],
41673       [
41674         "South Korea (대한민국)",
41675         "kr",
41676         "82"
41677       ],
41678       [
41679         "South Sudan (‫جنوب السودان‬‎)",
41680         "ss",
41681         "211"
41682       ],
41683       [
41684         "Spain (España)",
41685         "es",
41686         "34"
41687       ],
41688       [
41689         "Sri Lanka (ශ්‍රී ලංකාව)",
41690         "lk",
41691         "94"
41692       ],
41693       [
41694         "Sudan (‫السودان‬‎)",
41695         "sd",
41696         "249"
41697       ],
41698       [
41699         "Suriname",
41700         "sr",
41701         "597"
41702       ],
41703       [
41704         "Svalbard and Jan Mayen",
41705         "sj",
41706         "47",
41707         1
41708       ],
41709       [
41710         "Swaziland",
41711         "sz",
41712         "268"
41713       ],
41714       [
41715         "Sweden (Sverige)",
41716         "se",
41717         "46"
41718       ],
41719       [
41720         "Switzerland (Schweiz)",
41721         "ch",
41722         "41"
41723       ],
41724       [
41725         "Syria (‫سوريا‬‎)",
41726         "sy",
41727         "963"
41728       ],
41729       [
41730         "Taiwan (台灣)",
41731         "tw",
41732         "886"
41733       ],
41734       [
41735         "Tajikistan",
41736         "tj",
41737         "992"
41738       ],
41739       [
41740         "Tanzania",
41741         "tz",
41742         "255"
41743       ],
41744       [
41745         "Thailand (ไทย)",
41746         "th",
41747         "66"
41748       ],
41749       [
41750         "Timor-Leste",
41751         "tl",
41752         "670"
41753       ],
41754       [
41755         "Togo",
41756         "tg",
41757         "228"
41758       ],
41759       [
41760         "Tokelau",
41761         "tk",
41762         "690"
41763       ],
41764       [
41765         "Tonga",
41766         "to",
41767         "676"
41768       ],
41769       [
41770         "Trinidad and Tobago",
41771         "tt",
41772         "1868"
41773       ],
41774       [
41775         "Tunisia (‫تونس‬‎)",
41776         "tn",
41777         "216"
41778       ],
41779       [
41780         "Turkey (Türkiye)",
41781         "tr",
41782         "90"
41783       ],
41784       [
41785         "Turkmenistan",
41786         "tm",
41787         "993"
41788       ],
41789       [
41790         "Turks and Caicos Islands",
41791         "tc",
41792         "1649"
41793       ],
41794       [
41795         "Tuvalu",
41796         "tv",
41797         "688"
41798       ],
41799       [
41800         "U.S. Virgin Islands",
41801         "vi",
41802         "1340"
41803       ],
41804       [
41805         "Uganda",
41806         "ug",
41807         "256"
41808       ],
41809       [
41810         "Ukraine (Україна)",
41811         "ua",
41812         "380"
41813       ],
41814       [
41815         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
41816         "ae",
41817         "971"
41818       ],
41819       [
41820         "United Kingdom",
41821         "gb",
41822         "44",
41823         0
41824       ],
41825       [
41826         "United States",
41827         "us",
41828         "1",
41829         0
41830       ],
41831       [
41832         "Uruguay",
41833         "uy",
41834         "598"
41835       ],
41836       [
41837         "Uzbekistan (Oʻzbekiston)",
41838         "uz",
41839         "998"
41840       ],
41841       [
41842         "Vanuatu",
41843         "vu",
41844         "678"
41845       ],
41846       [
41847         "Vatican City (Città del Vaticano)",
41848         "va",
41849         "39",
41850         1
41851       ],
41852       [
41853         "Venezuela",
41854         "ve",
41855         "58"
41856       ],
41857       [
41858         "Vietnam (Việt Nam)",
41859         "vn",
41860         "84"
41861       ],
41862       [
41863         "Wallis and Futuna (Wallis-et-Futuna)",
41864         "wf",
41865         "681"
41866       ],
41867       [
41868         "Western Sahara (‫الصحراء الغربية‬‎)",
41869         "eh",
41870         "212",
41871         1
41872       ],
41873       [
41874         "Yemen (‫اليمن‬‎)",
41875         "ye",
41876         "967"
41877       ],
41878       [
41879         "Zambia",
41880         "zm",
41881         "260"
41882       ],
41883       [
41884         "Zimbabwe",
41885         "zw",
41886         "263"
41887       ],
41888       [
41889         "Åland Islands",
41890         "ax",
41891         "358",
41892         1
41893       ]
41894   ];
41895   
41896   return d;
41897 }/**
41898 *    This script refer to:
41899 *    Title: International Telephone Input
41900 *    Author: Jack O'Connor
41901 *    Code version:  v12.1.12
41902 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41903 **/
41904
41905 /**
41906  * @class Roo.bootstrap.PhoneInput
41907  * @extends Roo.bootstrap.TriggerField
41908  * An input with International dial-code selection
41909  
41910  * @cfg {String} defaultDialCode default '+852'
41911  * @cfg {Array} preferedCountries default []
41912   
41913  * @constructor
41914  * Create a new PhoneInput.
41915  * @param {Object} config Configuration options
41916  */
41917
41918 Roo.bootstrap.PhoneInput = function(config) {
41919     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
41920 };
41921
41922 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
41923         
41924         listWidth: undefined,
41925         
41926         selectedClass: 'active',
41927         
41928         invalidClass : "has-warning",
41929         
41930         validClass: 'has-success',
41931         
41932         allowed: '0123456789',
41933         
41934         max_length: 15,
41935         
41936         /**
41937          * @cfg {String} defaultDialCode The default dial code when initializing the input
41938          */
41939         defaultDialCode: '+852',
41940         
41941         /**
41942          * @cfg {Array} preferedCountries A list of iso2 in array (e.g. ['hk','us']). Those related countries will show at the top of the input's choices
41943          */
41944         preferedCountries: false,
41945         
41946         getAutoCreate : function()
41947         {
41948             var data = Roo.bootstrap.PhoneInputData();
41949             var align = this.labelAlign || this.parentLabelAlign();
41950             var id = Roo.id();
41951             
41952             this.allCountries = [];
41953             this.dialCodeMapping = [];
41954             
41955             for (var i = 0; i < data.length; i++) {
41956               var c = data[i];
41957               this.allCountries[i] = {
41958                 name: c[0],
41959                 iso2: c[1],
41960                 dialCode: c[2],
41961                 priority: c[3] || 0,
41962                 areaCodes: c[4] || null
41963               };
41964               this.dialCodeMapping[c[2]] = {
41965                   name: c[0],
41966                   iso2: c[1],
41967                   priority: c[3] || 0,
41968                   areaCodes: c[4] || null
41969               };
41970             }
41971             
41972             var cfg = {
41973                 cls: 'form-group',
41974                 cn: []
41975             };
41976             
41977             var input =  {
41978                 tag: 'input',
41979                 id : id,
41980                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
41981                 maxlength: this.max_length,
41982                 cls : 'form-control tel-input',
41983                 autocomplete: 'new-password'
41984             };
41985             
41986             var hiddenInput = {
41987                 tag: 'input',
41988                 type: 'hidden',
41989                 cls: 'hidden-tel-input'
41990             };
41991             
41992             if (this.name) {
41993                 hiddenInput.name = this.name;
41994             }
41995             
41996             if (this.disabled) {
41997                 input.disabled = true;
41998             }
41999             
42000             var flag_container = {
42001                 tag: 'div',
42002                 cls: 'flag-box',
42003                 cn: [
42004                     {
42005                         tag: 'div',
42006                         cls: 'flag'
42007                     },
42008                     {
42009                         tag: 'div',
42010                         cls: 'caret'
42011                     }
42012                 ]
42013             };
42014             
42015             var box = {
42016                 tag: 'div',
42017                 cls: this.hasFeedback ? 'has-feedback' : '',
42018                 cn: [
42019                     hiddenInput,
42020                     input,
42021                     {
42022                         tag: 'input',
42023                         cls: 'dial-code-holder',
42024                         disabled: true
42025                     }
42026                 ]
42027             };
42028             
42029             var container = {
42030                 cls: 'roo-select2-container input-group',
42031                 cn: [
42032                     flag_container,
42033                     box
42034                 ]
42035             };
42036             
42037             if (this.fieldLabel.length) {
42038                 var indicator = {
42039                     tag: 'i',
42040                     tooltip: 'This field is required'
42041                 };
42042                 
42043                 var label = {
42044                     tag: 'label',
42045                     'for':  id,
42046                     cls: 'control-label',
42047                     cn: []
42048                 };
42049                 
42050                 var label_text = {
42051                     tag: 'span',
42052                     html: this.fieldLabel
42053                 };
42054                 
42055                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42056                 label.cn = [
42057                     indicator,
42058                     label_text
42059                 ];
42060                 
42061                 if(this.indicatorpos == 'right') {
42062                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42063                     label.cn = [
42064                         label_text,
42065                         indicator
42066                     ];
42067                 }
42068                 
42069                 if(align == 'left') {
42070                     container = {
42071                         tag: 'div',
42072                         cn: [
42073                             container
42074                         ]
42075                     };
42076                     
42077                     if(this.labelWidth > 12){
42078                         label.style = "width: " + this.labelWidth + 'px';
42079                     }
42080                     if(this.labelWidth < 13 && this.labelmd == 0){
42081                         this.labelmd = this.labelWidth;
42082                     }
42083                     if(this.labellg > 0){
42084                         label.cls += ' col-lg-' + this.labellg;
42085                         input.cls += ' col-lg-' + (12 - this.labellg);
42086                     }
42087                     if(this.labelmd > 0){
42088                         label.cls += ' col-md-' + this.labelmd;
42089                         container.cls += ' col-md-' + (12 - this.labelmd);
42090                     }
42091                     if(this.labelsm > 0){
42092                         label.cls += ' col-sm-' + this.labelsm;
42093                         container.cls += ' col-sm-' + (12 - this.labelsm);
42094                     }
42095                     if(this.labelxs > 0){
42096                         label.cls += ' col-xs-' + this.labelxs;
42097                         container.cls += ' col-xs-' + (12 - this.labelxs);
42098                     }
42099                 }
42100             }
42101             
42102             cfg.cn = [
42103                 label,
42104                 container
42105             ];
42106             
42107             var settings = this;
42108             
42109             ['xs','sm','md','lg'].map(function(size){
42110                 if (settings[size]) {
42111                     cfg.cls += ' col-' + size + '-' + settings[size];
42112                 }
42113             });
42114             
42115             this.store = new Roo.data.Store({
42116                 proxy : new Roo.data.MemoryProxy({}),
42117                 reader : new Roo.data.JsonReader({
42118                     fields : [
42119                         {
42120                             'name' : 'name',
42121                             'type' : 'string'
42122                         },
42123                         {
42124                             'name' : 'iso2',
42125                             'type' : 'string'
42126                         },
42127                         {
42128                             'name' : 'dialCode',
42129                             'type' : 'string'
42130                         },
42131                         {
42132                             'name' : 'priority',
42133                             'type' : 'string'
42134                         },
42135                         {
42136                             'name' : 'areaCodes',
42137                             'type' : 'string'
42138                         }
42139                     ]
42140                 })
42141             });
42142             
42143             if(!this.preferedCountries) {
42144                 this.preferedCountries = [
42145                     'hk',
42146                     'gb',
42147                     'us'
42148                 ];
42149             }
42150             
42151             var p = this.preferedCountries.reverse();
42152             
42153             if(p) {
42154                 for (var i = 0; i < p.length; i++) {
42155                     for (var j = 0; j < this.allCountries.length; j++) {
42156                         if(this.allCountries[j].iso2 == p[i]) {
42157                             var t = this.allCountries[j];
42158                             this.allCountries.splice(j,1);
42159                             this.allCountries.unshift(t);
42160                         }
42161                     } 
42162                 }
42163             }
42164             
42165             this.store.proxy.data = {
42166                 success: true,
42167                 data: this.allCountries
42168             };
42169             
42170             return cfg;
42171         },
42172         
42173         initEvents : function()
42174         {
42175             this.createList();
42176             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42177             
42178             this.indicator = this.indicatorEl();
42179             this.flag = this.flagEl();
42180             this.dialCodeHolder = this.dialCodeHolderEl();
42181             
42182             this.trigger = this.el.select('div.flag-box',true).first();
42183             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42184             
42185             var _this = this;
42186             
42187             (function(){
42188                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42189                 _this.list.setWidth(lw);
42190             }).defer(100);
42191             
42192             this.list.on('mouseover', this.onViewOver, this);
42193             this.list.on('mousemove', this.onViewMove, this);
42194             this.inputEl().on("keyup", this.onKeyUp, this);
42195             this.inputEl().on("keypress", this.onKeyPress, this);
42196             
42197             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42198
42199             this.view = new Roo.View(this.list, this.tpl, {
42200                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42201             });
42202             
42203             this.view.on('click', this.onViewClick, this);
42204             this.setValue(this.defaultDialCode);
42205         },
42206         
42207         onTriggerClick : function(e)
42208         {
42209             Roo.log('trigger click');
42210             if(this.disabled){
42211                 return;
42212             }
42213             
42214             if(this.isExpanded()){
42215                 this.collapse();
42216                 this.hasFocus = false;
42217             }else {
42218                 this.store.load({});
42219                 this.hasFocus = true;
42220                 this.expand();
42221             }
42222         },
42223         
42224         isExpanded : function()
42225         {
42226             return this.list.isVisible();
42227         },
42228         
42229         collapse : function()
42230         {
42231             if(!this.isExpanded()){
42232                 return;
42233             }
42234             this.list.hide();
42235             Roo.get(document).un('mousedown', this.collapseIf, this);
42236             Roo.get(document).un('mousewheel', this.collapseIf, this);
42237             this.fireEvent('collapse', this);
42238             this.validate();
42239         },
42240         
42241         expand : function()
42242         {
42243             Roo.log('expand');
42244
42245             if(this.isExpanded() || !this.hasFocus){
42246                 return;
42247             }
42248             
42249             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42250             this.list.setWidth(lw);
42251             
42252             this.list.show();
42253             this.restrictHeight();
42254             
42255             Roo.get(document).on('mousedown', this.collapseIf, this);
42256             Roo.get(document).on('mousewheel', this.collapseIf, this);
42257             
42258             this.fireEvent('expand', this);
42259         },
42260         
42261         restrictHeight : function()
42262         {
42263             this.list.alignTo(this.inputEl(), this.listAlign);
42264             this.list.alignTo(this.inputEl(), this.listAlign);
42265         },
42266         
42267         onViewOver : function(e, t)
42268         {
42269             if(this.inKeyMode){
42270                 return;
42271             }
42272             var item = this.view.findItemFromChild(t);
42273             
42274             if(item){
42275                 var index = this.view.indexOf(item);
42276                 this.select(index, false);
42277             }
42278         },
42279
42280         // private
42281         onViewClick : function(view, doFocus, el, e)
42282         {
42283             var index = this.view.getSelectedIndexes()[0];
42284             
42285             var r = this.store.getAt(index);
42286             
42287             if(r){
42288                 this.onSelect(r, index);
42289             }
42290             if(doFocus !== false && !this.blockFocus){
42291                 this.inputEl().focus();
42292             }
42293         },
42294         
42295         onViewMove : function(e, t)
42296         {
42297             this.inKeyMode = false;
42298         },
42299         
42300         select : function(index, scrollIntoView)
42301         {
42302             this.selectedIndex = index;
42303             this.view.select(index);
42304             if(scrollIntoView !== false){
42305                 var el = this.view.getNode(index);
42306                 if(el){
42307                     this.list.scrollChildIntoView(el, false);
42308                 }
42309             }
42310         },
42311         
42312         createList : function()
42313         {
42314             this.list = Roo.get(document.body).createChild({
42315                 tag: 'ul',
42316                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42317                 style: 'display:none'
42318             });
42319             
42320             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42321         },
42322         
42323         collapseIf : function(e)
42324         {
42325             var in_combo  = e.within(this.el);
42326             var in_list =  e.within(this.list);
42327             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42328             
42329             if (in_combo || in_list || is_list) {
42330                 return;
42331             }
42332             this.collapse();
42333         },
42334         
42335         onSelect : function(record, index)
42336         {
42337             if(this.fireEvent('beforeselect', this, record, index) !== false){
42338                 
42339                 this.setFlagClass(record.data.iso2);
42340                 this.setDialCode(record.data.dialCode);
42341                 this.hasFocus = false;
42342                 this.collapse();
42343                 this.fireEvent('select', this, record, index);
42344             }
42345         },
42346         
42347         flagEl : function()
42348         {
42349             var flag = this.el.select('div.flag',true).first();
42350             if(!flag){
42351                 return false;
42352             }
42353             return flag;
42354         },
42355         
42356         dialCodeHolderEl : function()
42357         {
42358             var d = this.el.select('input.dial-code-holder',true).first();
42359             if(!d){
42360                 return false;
42361             }
42362             return d;
42363         },
42364         
42365         setDialCode : function(v)
42366         {
42367             this.dialCodeHolder.dom.value = '+'+v;
42368         },
42369         
42370         setFlagClass : function(n)
42371         {
42372             this.flag.dom.className = 'flag '+n;
42373         },
42374         
42375         getValue : function()
42376         {
42377             var v = this.inputEl().getValue();
42378             if(this.dialCodeHolder) {
42379                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42380             }
42381             return v;
42382         },
42383         
42384         setValue : function(v)
42385         {
42386             var d = this.getDialCode(v);
42387             
42388             //invalid dial code
42389             if(v.length == 0 || !d || d.length == 0) {
42390                 if(this.rendered){
42391                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42392                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42393                 }
42394                 return;
42395             }
42396             
42397             //valid dial code
42398             this.setFlagClass(this.dialCodeMapping[d].iso2);
42399             this.setDialCode(d);
42400             this.inputEl().dom.value = v.replace('+'+d,'');
42401             this.hiddenEl().dom.value = this.getValue();
42402             
42403             this.validate();
42404         },
42405         
42406         getDialCode : function(v)
42407         {
42408             v = v ||  '';
42409             
42410             if (v.length == 0) {
42411                 return this.dialCodeHolder.dom.value;
42412             }
42413             
42414             var dialCode = "";
42415             if (v.charAt(0) != "+") {
42416                 return false;
42417             }
42418             var numericChars = "";
42419             for (var i = 1; i < v.length; i++) {
42420               var c = v.charAt(i);
42421               if (!isNaN(c)) {
42422                 numericChars += c;
42423                 if (this.dialCodeMapping[numericChars]) {
42424                   dialCode = v.substr(1, i);
42425                 }
42426                 if (numericChars.length == 4) {
42427                   break;
42428                 }
42429               }
42430             }
42431             return dialCode;
42432         },
42433         
42434         reset : function()
42435         {
42436             this.setValue(this.defaultDialCode);
42437             this.validate();
42438         },
42439         
42440         hiddenEl : function()
42441         {
42442             return this.el.select('input.hidden-tel-input',true).first();
42443         },
42444         
42445         // after setting val
42446         onKeyUp : function(e){
42447             this.setValue(this.getValue());
42448         },
42449         
42450         onKeyPress : function(e){
42451             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42452                 e.stopEvent();
42453             }
42454         }
42455         
42456 });
42457 /**
42458  * @class Roo.bootstrap.MoneyField
42459  * @extends Roo.bootstrap.ComboBox
42460  * Bootstrap MoneyField class
42461  * 
42462  * @constructor
42463  * Create a new MoneyField.
42464  * @param {Object} config Configuration options
42465  */
42466
42467 Roo.bootstrap.MoneyField = function(config) {
42468     
42469     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42470     
42471 };
42472
42473 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42474     
42475     /**
42476      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42477      */
42478     allowDecimals : true,
42479     /**
42480      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42481      */
42482     decimalSeparator : ".",
42483     /**
42484      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42485      */
42486     decimalPrecision : 0,
42487     /**
42488      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42489      */
42490     allowNegative : true,
42491     /**
42492      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42493      */
42494     allowZero: true,
42495     /**
42496      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42497      */
42498     minValue : Number.NEGATIVE_INFINITY,
42499     /**
42500      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42501      */
42502     maxValue : Number.MAX_VALUE,
42503     /**
42504      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42505      */
42506     minText : "The minimum value for this field is {0}",
42507     /**
42508      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42509      */
42510     maxText : "The maximum value for this field is {0}",
42511     /**
42512      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
42513      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42514      */
42515     nanText : "{0} is not a valid number",
42516     /**
42517      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42518      */
42519     castInt : true,
42520     /**
42521      * @cfg {String} defaults currency of the MoneyField
42522      * value should be in lkey
42523      */
42524     defaultCurrency : false,
42525     /**
42526      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42527      */
42528     thousandsDelimiter : false,
42529     /**
42530      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42531      */
42532     max_length: false,
42533     
42534     inputlg : 9,
42535     inputmd : 9,
42536     inputsm : 9,
42537     inputxs : 6,
42538     
42539     store : false,
42540     
42541     getAutoCreate : function()
42542     {
42543         var align = this.labelAlign || this.parentLabelAlign();
42544         
42545         var id = Roo.id();
42546
42547         var cfg = {
42548             cls: 'form-group',
42549             cn: []
42550         };
42551
42552         var input =  {
42553             tag: 'input',
42554             id : id,
42555             cls : 'form-control roo-money-amount-input',
42556             autocomplete: 'new-password'
42557         };
42558         
42559         var hiddenInput = {
42560             tag: 'input',
42561             type: 'hidden',
42562             id: Roo.id(),
42563             cls: 'hidden-number-input'
42564         };
42565         
42566         if(this.max_length) {
42567             input.maxlength = this.max_length; 
42568         }
42569         
42570         if (this.name) {
42571             hiddenInput.name = this.name;
42572         }
42573
42574         if (this.disabled) {
42575             input.disabled = true;
42576         }
42577
42578         var clg = 12 - this.inputlg;
42579         var cmd = 12 - this.inputmd;
42580         var csm = 12 - this.inputsm;
42581         var cxs = 12 - this.inputxs;
42582         
42583         var container = {
42584             tag : 'div',
42585             cls : 'row roo-money-field',
42586             cn : [
42587                 {
42588                     tag : 'div',
42589                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42590                     cn : [
42591                         {
42592                             tag : 'div',
42593                             cls: 'roo-select2-container input-group',
42594                             cn: [
42595                                 {
42596                                     tag : 'input',
42597                                     cls : 'form-control roo-money-currency-input',
42598                                     autocomplete: 'new-password',
42599                                     readOnly : 1,
42600                                     name : this.currencyName
42601                                 },
42602                                 {
42603                                     tag :'span',
42604                                     cls : 'input-group-addon',
42605                                     cn : [
42606                                         {
42607                                             tag: 'span',
42608                                             cls: 'caret'
42609                                         }
42610                                     ]
42611                                 }
42612                             ]
42613                         }
42614                     ]
42615                 },
42616                 {
42617                     tag : 'div',
42618                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42619                     cn : [
42620                         {
42621                             tag: 'div',
42622                             cls: this.hasFeedback ? 'has-feedback' : '',
42623                             cn: [
42624                                 input
42625                             ]
42626                         }
42627                     ]
42628                 }
42629             ]
42630             
42631         };
42632         
42633         if (this.fieldLabel.length) {
42634             var indicator = {
42635                 tag: 'i',
42636                 tooltip: 'This field is required'
42637             };
42638
42639             var label = {
42640                 tag: 'label',
42641                 'for':  id,
42642                 cls: 'control-label',
42643                 cn: []
42644             };
42645
42646             var label_text = {
42647                 tag: 'span',
42648                 html: this.fieldLabel
42649             };
42650
42651             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42652             label.cn = [
42653                 indicator,
42654                 label_text
42655             ];
42656
42657             if(this.indicatorpos == 'right') {
42658                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42659                 label.cn = [
42660                     label_text,
42661                     indicator
42662                 ];
42663             }
42664
42665             if(align == 'left') {
42666                 container = {
42667                     tag: 'div',
42668                     cn: [
42669                         container
42670                     ]
42671                 };
42672
42673                 if(this.labelWidth > 12){
42674                     label.style = "width: " + this.labelWidth + 'px';
42675                 }
42676                 if(this.labelWidth < 13 && this.labelmd == 0){
42677                     this.labelmd = this.labelWidth;
42678                 }
42679                 if(this.labellg > 0){
42680                     label.cls += ' col-lg-' + this.labellg;
42681                     input.cls += ' col-lg-' + (12 - this.labellg);
42682                 }
42683                 if(this.labelmd > 0){
42684                     label.cls += ' col-md-' + this.labelmd;
42685                     container.cls += ' col-md-' + (12 - this.labelmd);
42686                 }
42687                 if(this.labelsm > 0){
42688                     label.cls += ' col-sm-' + this.labelsm;
42689                     container.cls += ' col-sm-' + (12 - this.labelsm);
42690                 }
42691                 if(this.labelxs > 0){
42692                     label.cls += ' col-xs-' + this.labelxs;
42693                     container.cls += ' col-xs-' + (12 - this.labelxs);
42694                 }
42695             }
42696         }
42697
42698         cfg.cn = [
42699             label,
42700             container,
42701             hiddenInput
42702         ];
42703         
42704         var settings = this;
42705
42706         ['xs','sm','md','lg'].map(function(size){
42707             if (settings[size]) {
42708                 cfg.cls += ' col-' + size + '-' + settings[size];
42709             }
42710         });
42711         
42712         return cfg;
42713     },
42714     
42715     initEvents : function()
42716     {
42717         this.indicator = this.indicatorEl();
42718         
42719         this.initCurrencyEvent();
42720         
42721         this.initNumberEvent();
42722     },
42723     
42724     initCurrencyEvent : function()
42725     {
42726         if (!this.store) {
42727             throw "can not find store for combo";
42728         }
42729         
42730         this.store = Roo.factory(this.store, Roo.data);
42731         this.store.parent = this;
42732         
42733         this.createList();
42734         
42735         this.triggerEl = this.el.select('.input-group-addon', true).first();
42736         
42737         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42738         
42739         var _this = this;
42740         
42741         (function(){
42742             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42743             _this.list.setWidth(lw);
42744         }).defer(100);
42745         
42746         this.list.on('mouseover', this.onViewOver, this);
42747         this.list.on('mousemove', this.onViewMove, this);
42748         this.list.on('scroll', this.onViewScroll, this);
42749         
42750         if(!this.tpl){
42751             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42752         }
42753         
42754         this.view = new Roo.View(this.list, this.tpl, {
42755             singleSelect:true, store: this.store, selectedClass: this.selectedClass
42756         });
42757         
42758         this.view.on('click', this.onViewClick, this);
42759         
42760         this.store.on('beforeload', this.onBeforeLoad, this);
42761         this.store.on('load', this.onLoad, this);
42762         this.store.on('loadexception', this.onLoadException, this);
42763         
42764         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
42765             "up" : function(e){
42766                 this.inKeyMode = true;
42767                 this.selectPrev();
42768             },
42769
42770             "down" : function(e){
42771                 if(!this.isExpanded()){
42772                     this.onTriggerClick();
42773                 }else{
42774                     this.inKeyMode = true;
42775                     this.selectNext();
42776                 }
42777             },
42778
42779             "enter" : function(e){
42780                 this.collapse();
42781                 
42782                 if(this.fireEvent("specialkey", this, e)){
42783                     this.onViewClick(false);
42784                 }
42785                 
42786                 return true;
42787             },
42788
42789             "esc" : function(e){
42790                 this.collapse();
42791             },
42792
42793             "tab" : function(e){
42794                 this.collapse();
42795                 
42796                 if(this.fireEvent("specialkey", this, e)){
42797                     this.onViewClick(false);
42798                 }
42799                 
42800                 return true;
42801             },
42802
42803             scope : this,
42804
42805             doRelay : function(foo, bar, hname){
42806                 if(hname == 'down' || this.scope.isExpanded()){
42807                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42808                 }
42809                 return true;
42810             },
42811
42812             forceKeyDown: true
42813         });
42814         
42815         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
42816         
42817     },
42818     
42819     initNumberEvent : function(e)
42820     {
42821         this.inputEl().on("keydown" , this.fireKey,  this);
42822         this.inputEl().on("focus", this.onFocus,  this);
42823         this.inputEl().on("blur", this.onBlur,  this);
42824         
42825         this.inputEl().relayEvent('keyup', this);
42826         
42827         if(this.indicator){
42828             this.indicator.addClass('invisible');
42829         }
42830  
42831         this.originalValue = this.getValue();
42832         
42833         if(this.validationEvent == 'keyup'){
42834             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
42835             this.inputEl().on('keyup', this.filterValidation, this);
42836         }
42837         else if(this.validationEvent !== false){
42838             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
42839         }
42840         
42841         if(this.selectOnFocus){
42842             this.on("focus", this.preFocus, this);
42843             
42844         }
42845         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
42846             this.inputEl().on("keypress", this.filterKeys, this);
42847         } else {
42848             this.inputEl().relayEvent('keypress', this);
42849         }
42850         
42851         var allowed = "0123456789";
42852         
42853         if(this.allowDecimals){
42854             allowed += this.decimalSeparator;
42855         }
42856         
42857         if(this.allowNegative){
42858             allowed += "-";
42859         }
42860         
42861         if(this.thousandsDelimiter) {
42862             allowed += ",";
42863         }
42864         
42865         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
42866         
42867         var keyPress = function(e){
42868             
42869             var k = e.getKey();
42870             
42871             var c = e.getCharCode();
42872             
42873             if(
42874                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
42875                     allowed.indexOf(String.fromCharCode(c)) === -1
42876             ){
42877                 e.stopEvent();
42878                 return;
42879             }
42880             
42881             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
42882                 return;
42883             }
42884             
42885             if(allowed.indexOf(String.fromCharCode(c)) === -1){
42886                 e.stopEvent();
42887             }
42888         };
42889         
42890         this.inputEl().on("keypress", keyPress, this);
42891         
42892     },
42893     
42894     onTriggerClick : function(e)
42895     {   
42896         if(this.disabled){
42897             return;
42898         }
42899         
42900         this.page = 0;
42901         this.loadNext = false;
42902         
42903         if(this.isExpanded()){
42904             this.collapse();
42905             return;
42906         }
42907         
42908         this.hasFocus = true;
42909         
42910         if(this.triggerAction == 'all') {
42911             this.doQuery(this.allQuery, true);
42912             return;
42913         }
42914         
42915         this.doQuery(this.getRawValue());
42916     },
42917     
42918     getCurrency : function()
42919     {   
42920         var v = this.currencyEl().getValue();
42921         
42922         return v;
42923     },
42924     
42925     restrictHeight : function()
42926     {
42927         this.list.alignTo(this.currencyEl(), this.listAlign);
42928         this.list.alignTo(this.currencyEl(), this.listAlign);
42929     },
42930     
42931     onViewClick : function(view, doFocus, el, e)
42932     {
42933         var index = this.view.getSelectedIndexes()[0];
42934         
42935         var r = this.store.getAt(index);
42936         
42937         if(r){
42938             this.onSelect(r, index);
42939         }
42940     },
42941     
42942     onSelect : function(record, index){
42943         
42944         if(this.fireEvent('beforeselect', this, record, index) !== false){
42945         
42946             this.setFromCurrencyData(index > -1 ? record.data : false);
42947             
42948             this.collapse();
42949             
42950             this.fireEvent('select', this, record, index);
42951         }
42952     },
42953     
42954     setFromCurrencyData : function(o)
42955     {
42956         var currency = '';
42957         
42958         this.lastCurrency = o;
42959         
42960         if (this.currencyField) {
42961             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
42962         } else {
42963             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
42964         }
42965         
42966         this.lastSelectionText = currency;
42967         
42968         //setting default currency
42969         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
42970             this.setCurrency(this.defaultCurrency);
42971             return;
42972         }
42973         
42974         this.setCurrency(currency);
42975     },
42976     
42977     setFromData : function(o)
42978     {
42979         var c = {};
42980         
42981         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
42982         
42983         this.setFromCurrencyData(c);
42984         
42985         var value = '';
42986         
42987         if (this.name) {
42988             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
42989         } else {
42990             Roo.log('no value set for '+ (this.name ? this.name : this.id));
42991         }
42992         
42993         this.setValue(value);
42994         
42995     },
42996     
42997     setCurrency : function(v)
42998     {   
42999         this.currencyValue = v;
43000         
43001         if(this.rendered){
43002             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43003             this.validate();
43004         }
43005     },
43006     
43007     setValue : function(v)
43008     {
43009         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43010         
43011         this.value = v;
43012         
43013         if(this.rendered){
43014             
43015             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43016             
43017             this.inputEl().dom.value = (v == '') ? '' :
43018                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43019             
43020             if(!this.allowZero && v === '0') {
43021                 this.hiddenEl().dom.value = '';
43022                 this.inputEl().dom.value = '';
43023             }
43024             
43025             this.validate();
43026         }
43027     },
43028     
43029     getRawValue : function()
43030     {
43031         var v = this.inputEl().getValue();
43032         
43033         return v;
43034     },
43035     
43036     getValue : function()
43037     {
43038         return this.fixPrecision(this.parseValue(this.getRawValue()));
43039     },
43040     
43041     parseValue : function(value)
43042     {
43043         if(this.thousandsDelimiter) {
43044             value += "";
43045             r = new RegExp(",", "g");
43046             value = value.replace(r, "");
43047         }
43048         
43049         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43050         return isNaN(value) ? '' : value;
43051         
43052     },
43053     
43054     fixPrecision : function(value)
43055     {
43056         if(this.thousandsDelimiter) {
43057             value += "";
43058             r = new RegExp(",", "g");
43059             value = value.replace(r, "");
43060         }
43061         
43062         var nan = isNaN(value);
43063         
43064         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43065             return nan ? '' : value;
43066         }
43067         return parseFloat(value).toFixed(this.decimalPrecision);
43068     },
43069     
43070     decimalPrecisionFcn : function(v)
43071     {
43072         return Math.floor(v);
43073     },
43074     
43075     validateValue : function(value)
43076     {
43077         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43078             return false;
43079         }
43080         
43081         var num = this.parseValue(value);
43082         
43083         if(isNaN(num)){
43084             this.markInvalid(String.format(this.nanText, value));
43085             return false;
43086         }
43087         
43088         if(num < this.minValue){
43089             this.markInvalid(String.format(this.minText, this.minValue));
43090             return false;
43091         }
43092         
43093         if(num > this.maxValue){
43094             this.markInvalid(String.format(this.maxText, this.maxValue));
43095             return false;
43096         }
43097         
43098         return true;
43099     },
43100     
43101     validate : function()
43102     {
43103         if(this.disabled || this.allowBlank){
43104             this.markValid();
43105             return true;
43106         }
43107         
43108         var currency = this.getCurrency();
43109         
43110         if(this.validateValue(this.getRawValue()) && currency.length){
43111             this.markValid();
43112             return true;
43113         }
43114         
43115         this.markInvalid();
43116         return false;
43117     },
43118     
43119     getName: function()
43120     {
43121         return this.name;
43122     },
43123     
43124     beforeBlur : function()
43125     {
43126         if(!this.castInt){
43127             return;
43128         }
43129         
43130         var v = this.parseValue(this.getRawValue());
43131         
43132         if(v || v == 0){
43133             this.setValue(v);
43134         }
43135     },
43136     
43137     onBlur : function()
43138     {
43139         this.beforeBlur();
43140         
43141         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43142             //this.el.removeClass(this.focusClass);
43143         }
43144         
43145         this.hasFocus = false;
43146         
43147         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43148             this.validate();
43149         }
43150         
43151         var v = this.getValue();
43152         
43153         if(String(v) !== String(this.startValue)){
43154             this.fireEvent('change', this, v, this.startValue);
43155         }
43156         
43157         this.fireEvent("blur", this);
43158     },
43159     
43160     inputEl : function()
43161     {
43162         return this.el.select('.roo-money-amount-input', true).first();
43163     },
43164     
43165     currencyEl : function()
43166     {
43167         return this.el.select('.roo-money-currency-input', true).first();
43168     },
43169     
43170     hiddenEl : function()
43171     {
43172         return this.el.select('input.hidden-number-input',true).first();
43173     }
43174     
43175 });/**
43176  * @class Roo.bootstrap.BezierSignature
43177  * @extends Roo.bootstrap.Component
43178  * Bootstrap BezierSignature class
43179  * This script refer to:
43180  *    Title: Signature Pad
43181  *    Author: szimek
43182  *    Availability: https://github.com/szimek/signature_pad
43183  *
43184  * @constructor
43185  * Create a new BezierSignature
43186  * @param {Object} config The config object
43187  */
43188
43189 Roo.bootstrap.BezierSignature = function(config){
43190     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43191     this.addEvents({
43192         "resize" : true
43193     });
43194 };
43195
43196 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43197 {
43198      
43199     curve_data: [],
43200     
43201     is_empty: true,
43202     
43203     mouse_btn_down: true,
43204     
43205     /**
43206      * @cfg {int} canvas height
43207      */
43208     canvas_height: '200px',
43209     
43210     /**
43211      * @cfg {float|function} Radius of a single dot.
43212      */ 
43213     dot_size: false,
43214     
43215     /**
43216      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43217      */
43218     min_width: 0.5,
43219     
43220     /**
43221      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43222      */
43223     max_width: 2.5,
43224     
43225     /**
43226      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43227      */
43228     throttle: 16,
43229     
43230     /**
43231      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43232      */
43233     min_distance: 5,
43234     
43235     /**
43236      * @cfg {string} Color used to clear the background. Can be any color format accepted by context.fillStyle. Defaults to "rgba(0,0,0,0)" (transparent black). Use a non-transparent color e.g. "rgb(255,255,255)" (opaque white) if you'd like to save signatures as JPEG images.
43237      */
43238     bg_color: 'rgba(0, 0, 0, 0)',
43239     
43240     /**
43241      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43242      */
43243     dot_color: 'black',
43244     
43245     /**
43246      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43247      */ 
43248     velocity_filter_weight: 0.7,
43249     
43250     /**
43251      * @cfg {function} Callback when stroke begin. 
43252      */
43253     onBegin: false,
43254     
43255     /**
43256      * @cfg {function} Callback when stroke end.
43257      */
43258     onEnd: false,
43259     
43260     getAutoCreate : function()
43261     {
43262         var cls = 'roo-signature column';
43263         
43264         if(this.cls){
43265             cls += ' ' + this.cls;
43266         }
43267         
43268         var col_sizes = [
43269             'lg',
43270             'md',
43271             'sm',
43272             'xs'
43273         ];
43274         
43275         for(var i = 0; i < col_sizes.length; i++) {
43276             if(this[col_sizes[i]]) {
43277                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43278             }
43279         }
43280         
43281         var cfg = {
43282             tag: 'div',
43283             cls: cls,
43284             cn: [
43285                 {
43286                     tag: 'div',
43287                     cls: 'roo-signature-body',
43288                     cn: [
43289                         {
43290                             tag: 'canvas',
43291                             cls: 'roo-signature-body-canvas',
43292                             height: this.canvas_height,
43293                             width: this.canvas_width
43294                         }
43295                     ]
43296                 },
43297                 {
43298                     tag: 'input',
43299                     type: 'file',
43300                     style: 'display: none'
43301                 }
43302             ]
43303         };
43304         
43305         return cfg;
43306     },
43307     
43308     initEvents: function() 
43309     {
43310         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43311         
43312         var canvas = this.canvasEl();
43313         
43314         // mouse && touch event swapping...
43315         canvas.dom.style.touchAction = 'none';
43316         canvas.dom.style.msTouchAction = 'none';
43317         
43318         this.mouse_btn_down = false;
43319         canvas.on('mousedown', this._handleMouseDown, this);
43320         canvas.on('mousemove', this._handleMouseMove, this);
43321         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43322         
43323         if (window.PointerEvent) {
43324             canvas.on('pointerdown', this._handleMouseDown, this);
43325             canvas.on('pointermove', this._handleMouseMove, this);
43326             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43327         }
43328         
43329         if ('ontouchstart' in window) {
43330             canvas.on('touchstart', this._handleTouchStart, this);
43331             canvas.on('touchmove', this._handleTouchMove, this);
43332             canvas.on('touchend', this._handleTouchEnd, this);
43333         }
43334         
43335         Roo.EventManager.onWindowResize(this.resize, this, true);
43336         
43337         // file input event
43338         this.fileEl().on('change', this.uploadImage, this);
43339         
43340         this.clear();
43341         
43342         this.resize();
43343     },
43344     
43345     resize: function(){
43346         
43347         var canvas = this.canvasEl().dom;
43348         var ctx = this.canvasElCtx();
43349         var img_data = false;
43350         
43351         if(canvas.width > 0) {
43352             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43353         }
43354         // setting canvas width will clean img data
43355         canvas.width = 0;
43356         
43357         var style = window.getComputedStyle ? 
43358             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43359             
43360         var padding_left = parseInt(style.paddingLeft) || 0;
43361         var padding_right = parseInt(style.paddingRight) || 0;
43362         
43363         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43364         
43365         if(img_data) {
43366             ctx.putImageData(img_data, 0, 0);
43367         }
43368     },
43369     
43370     _handleMouseDown: function(e)
43371     {
43372         if (e.browserEvent.which === 1) {
43373             this.mouse_btn_down = true;
43374             this.strokeBegin(e);
43375         }
43376     },
43377     
43378     _handleMouseMove: function (e)
43379     {
43380         if (this.mouse_btn_down) {
43381             this.strokeMoveUpdate(e);
43382         }
43383     },
43384     
43385     _handleMouseUp: function (e)
43386     {
43387         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43388             this.mouse_btn_down = false;
43389             this.strokeEnd(e);
43390         }
43391     },
43392     
43393     _handleTouchStart: function (e) {
43394         
43395         e.preventDefault();
43396         if (e.browserEvent.targetTouches.length === 1) {
43397             // var touch = e.browserEvent.changedTouches[0];
43398             // this.strokeBegin(touch);
43399             
43400              this.strokeBegin(e); // assume e catching the correct xy...
43401         }
43402     },
43403     
43404     _handleTouchMove: function (e) {
43405         e.preventDefault();
43406         // var touch = event.targetTouches[0];
43407         // _this._strokeMoveUpdate(touch);
43408         this.strokeMoveUpdate(e);
43409     },
43410     
43411     _handleTouchEnd: function (e) {
43412         var wasCanvasTouched = e.target === this.canvasEl().dom;
43413         if (wasCanvasTouched) {
43414             e.preventDefault();
43415             // var touch = event.changedTouches[0];
43416             // _this._strokeEnd(touch);
43417             this.strokeEnd(e);
43418         }
43419     },
43420     
43421     reset: function () {
43422         this._lastPoints = [];
43423         this._lastVelocity = 0;
43424         this._lastWidth = (this.min_width + this.max_width) / 2;
43425         this.canvasElCtx().fillStyle = this.dot_color;
43426     },
43427     
43428     strokeMoveUpdate: function(e)
43429     {
43430         this.strokeUpdate(e);
43431         
43432         if (this.throttle) {
43433             this.throttleStroke(this.strokeUpdate, this.throttle);
43434         }
43435         else {
43436             this.strokeUpdate(e);
43437         }
43438     },
43439     
43440     strokeBegin: function(e)
43441     {
43442         var newPointGroup = {
43443             color: this.dot_color,
43444             points: []
43445         };
43446         
43447         if (typeof this.onBegin === 'function') {
43448             this.onBegin(e);
43449         }
43450         
43451         this.curve_data.push(newPointGroup);
43452         this.reset();
43453         this.strokeUpdate(e);
43454     },
43455     
43456     strokeUpdate: function(e)
43457     {
43458         var rect = this.canvasEl().dom.getBoundingClientRect();
43459         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43460         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43461         var lastPoints = lastPointGroup.points;
43462         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43463         var isLastPointTooClose = lastPoint
43464             ? point.distanceTo(lastPoint) <= this.min_distance
43465             : false;
43466         var color = lastPointGroup.color;
43467         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43468             var curve = this.addPoint(point);
43469             if (!lastPoint) {
43470                 this.drawDot({color: color, point: point});
43471             }
43472             else if (curve) {
43473                 this.drawCurve({color: color, curve: curve});
43474             }
43475             lastPoints.push({
43476                 time: point.time,
43477                 x: point.x,
43478                 y: point.y
43479             });
43480         }
43481     },
43482     
43483     strokeEnd: function(e)
43484     {
43485         this.strokeUpdate(e);
43486         if (typeof this.onEnd === 'function') {
43487             this.onEnd(e);
43488         }
43489     },
43490     
43491     addPoint:  function (point) {
43492         var _lastPoints = this._lastPoints;
43493         _lastPoints.push(point);
43494         if (_lastPoints.length > 2) {
43495             if (_lastPoints.length === 3) {
43496                 _lastPoints.unshift(_lastPoints[0]);
43497             }
43498             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43499             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43500             _lastPoints.shift();
43501             return curve;
43502         }
43503         return null;
43504     },
43505     
43506     calculateCurveWidths: function (startPoint, endPoint) {
43507         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43508             (1 - this.velocity_filter_weight) * this._lastVelocity;
43509
43510         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43511         var widths = {
43512             end: newWidth,
43513             start: this._lastWidth
43514         };
43515         
43516         this._lastVelocity = velocity;
43517         this._lastWidth = newWidth;
43518         return widths;
43519     },
43520     
43521     drawDot: function (_a) {
43522         var color = _a.color, point = _a.point;
43523         var ctx = this.canvasElCtx();
43524         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43525         ctx.beginPath();
43526         this.drawCurveSegment(point.x, point.y, width);
43527         ctx.closePath();
43528         ctx.fillStyle = color;
43529         ctx.fill();
43530     },
43531     
43532     drawCurve: function (_a) {
43533         var color = _a.color, curve = _a.curve;
43534         var ctx = this.canvasElCtx();
43535         var widthDelta = curve.endWidth - curve.startWidth;
43536         var drawSteps = Math.floor(curve.length()) * 2;
43537         ctx.beginPath();
43538         ctx.fillStyle = color;
43539         for (var i = 0; i < drawSteps; i += 1) {
43540         var t = i / drawSteps;
43541         var tt = t * t;
43542         var ttt = tt * t;
43543         var u = 1 - t;
43544         var uu = u * u;
43545         var uuu = uu * u;
43546         var x = uuu * curve.startPoint.x;
43547         x += 3 * uu * t * curve.control1.x;
43548         x += 3 * u * tt * curve.control2.x;
43549         x += ttt * curve.endPoint.x;
43550         var y = uuu * curve.startPoint.y;
43551         y += 3 * uu * t * curve.control1.y;
43552         y += 3 * u * tt * curve.control2.y;
43553         y += ttt * curve.endPoint.y;
43554         var width = curve.startWidth + ttt * widthDelta;
43555         this.drawCurveSegment(x, y, width);
43556         }
43557         ctx.closePath();
43558         ctx.fill();
43559     },
43560     
43561     drawCurveSegment: function (x, y, width) {
43562         var ctx = this.canvasElCtx();
43563         ctx.moveTo(x, y);
43564         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43565         this.is_empty = false;
43566     },
43567     
43568     clear: function()
43569     {
43570         var ctx = this.canvasElCtx();
43571         var canvas = this.canvasEl().dom;
43572         ctx.fillStyle = this.bg_color;
43573         ctx.clearRect(0, 0, canvas.width, canvas.height);
43574         ctx.fillRect(0, 0, canvas.width, canvas.height);
43575         this.curve_data = [];
43576         this.reset();
43577         this.is_empty = true;
43578     },
43579     
43580     fileEl: function()
43581     {
43582         return  this.el.select('input',true).first();
43583     },
43584     
43585     canvasEl: function()
43586     {
43587         return this.el.select('canvas',true).first();
43588     },
43589     
43590     canvasElCtx: function()
43591     {
43592         return this.el.select('canvas',true).first().dom.getContext('2d');
43593     },
43594     
43595     getImage: function(type)
43596     {
43597         if(this.is_empty) {
43598             return false;
43599         }
43600         
43601         // encryption ?
43602         return this.canvasEl().dom.toDataURL('image/'+type, 1);
43603     },
43604     
43605     drawFromImage: function(img_src)
43606     {
43607         var img = new Image();
43608         
43609         img.onload = function(){
43610             this.canvasElCtx().drawImage(img, 0, 0);
43611         }.bind(this);
43612         
43613         img.src = img_src;
43614         
43615         this.is_empty = false;
43616     },
43617     
43618     selectImage: function()
43619     {
43620         this.fileEl().dom.click();
43621     },
43622     
43623     uploadImage: function(e)
43624     {
43625         var reader = new FileReader();
43626         
43627         reader.onload = function(e){
43628             var img = new Image();
43629             img.onload = function(){
43630                 this.reset();
43631                 this.canvasElCtx().drawImage(img, 0, 0);
43632             }.bind(this);
43633             img.src = e.target.result;
43634         }.bind(this);
43635         
43636         reader.readAsDataURL(e.target.files[0]);
43637     },
43638     
43639     // Bezier Point Constructor
43640     Point: (function () {
43641         function Point(x, y, time) {
43642             this.x = x;
43643             this.y = y;
43644             this.time = time || Date.now();
43645         }
43646         Point.prototype.distanceTo = function (start) {
43647             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43648         };
43649         Point.prototype.equals = function (other) {
43650             return this.x === other.x && this.y === other.y && this.time === other.time;
43651         };
43652         Point.prototype.velocityFrom = function (start) {
43653             return this.time !== start.time
43654             ? this.distanceTo(start) / (this.time - start.time)
43655             : 0;
43656         };
43657         return Point;
43658     }()),
43659     
43660     
43661     // Bezier Constructor
43662     Bezier: (function () {
43663         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43664             this.startPoint = startPoint;
43665             this.control2 = control2;
43666             this.control1 = control1;
43667             this.endPoint = endPoint;
43668             this.startWidth = startWidth;
43669             this.endWidth = endWidth;
43670         }
43671         Bezier.fromPoints = function (points, widths, scope) {
43672             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43673             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43674             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43675         };
43676         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43677             var dx1 = s1.x - s2.x;
43678             var dy1 = s1.y - s2.y;
43679             var dx2 = s2.x - s3.x;
43680             var dy2 = s2.y - s3.y;
43681             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43682             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43683             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43684             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43685             var dxm = m1.x - m2.x;
43686             var dym = m1.y - m2.y;
43687             var k = l2 / (l1 + l2);
43688             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43689             var tx = s2.x - cm.x;
43690             var ty = s2.y - cm.y;
43691             return {
43692                 c1: new scope.Point(m1.x + tx, m1.y + ty),
43693                 c2: new scope.Point(m2.x + tx, m2.y + ty)
43694             };
43695         };
43696         Bezier.prototype.length = function () {
43697             var steps = 10;
43698             var length = 0;
43699             var px;
43700             var py;
43701             for (var i = 0; i <= steps; i += 1) {
43702                 var t = i / steps;
43703                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43704                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43705                 if (i > 0) {
43706                     var xdiff = cx - px;
43707                     var ydiff = cy - py;
43708                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43709                 }
43710                 px = cx;
43711                 py = cy;
43712             }
43713             return length;
43714         };
43715         Bezier.prototype.point = function (t, start, c1, c2, end) {
43716             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43717             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43718             + (3.0 * c2 * (1.0 - t) * t * t)
43719             + (end * t * t * t);
43720         };
43721         return Bezier;
43722     }()),
43723     
43724     throttleStroke: function(fn, wait) {
43725       if (wait === void 0) { wait = 250; }
43726       var previous = 0;
43727       var timeout = null;
43728       var result;
43729       var storedContext;
43730       var storedArgs;
43731       var later = function () {
43732           previous = Date.now();
43733           timeout = null;
43734           result = fn.apply(storedContext, storedArgs);
43735           if (!timeout) {
43736               storedContext = null;
43737               storedArgs = [];
43738           }
43739       };
43740       return function wrapper() {
43741           var args = [];
43742           for (var _i = 0; _i < arguments.length; _i++) {
43743               args[_i] = arguments[_i];
43744           }
43745           var now = Date.now();
43746           var remaining = wait - (now - previous);
43747           storedContext = this;
43748           storedArgs = args;
43749           if (remaining <= 0 || remaining > wait) {
43750               if (timeout) {
43751                   clearTimeout(timeout);
43752                   timeout = null;
43753               }
43754               previous = now;
43755               result = fn.apply(storedContext, storedArgs);
43756               if (!timeout) {
43757                   storedContext = null;
43758                   storedArgs = [];
43759               }
43760           }
43761           else if (!timeout) {
43762               timeout = window.setTimeout(later, remaining);
43763           }
43764           return result;
43765       };
43766   }
43767   
43768 });
43769
43770  
43771
43772