1fffa4cef7dac51b40838dda95ad77bbb3bc2351
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets, function(s) {
10                     if ( s.href  && s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })(); /*
16  * Based on:
17  * Ext JS Library 1.1.1
18  * Copyright(c) 2006-2007, Ext JS, LLC.
19  *
20  * Originally Released Under LGPL - original licence link has changed is not relivant.
21  *
22  * Fork - LGPL
23  * <script type="text/javascript">
24  */
25
26
27 /**
28  * @class Roo.Shadow
29  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
30  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
31  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
32  * @constructor
33  * Create a new Shadow
34  * @param {Object} config The config object
35  */
36 Roo.Shadow = function(config){
37     Roo.apply(this, config);
38     if(typeof this.mode != "string"){
39         this.mode = this.defaultMode;
40     }
41     var o = this.offset, a = {h: 0};
42     var rad = Math.floor(this.offset/2);
43     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
44         case "drop":
45             a.w = 0;
46             a.l = a.t = o;
47             a.t -= 1;
48             if(Roo.isIE){
49                 a.l -= this.offset + rad;
50                 a.t -= this.offset + rad;
51                 a.w -= rad;
52                 a.h -= rad;
53                 a.t += 1;
54             }
55         break;
56         case "sides":
57             a.w = (o*2);
58             a.l = -o;
59             a.t = o-1;
60             if(Roo.isIE){
61                 a.l -= (this.offset - rad);
62                 a.t -= this.offset + rad;
63                 a.l += 1;
64                 a.w -= (this.offset - rad)*2;
65                 a.w -= rad + 1;
66                 a.h -= 1;
67             }
68         break;
69         case "frame":
70             a.w = a.h = (o*2);
71             a.l = a.t = -o;
72             a.t += 1;
73             a.h -= 2;
74             if(Roo.isIE){
75                 a.l -= (this.offset - rad);
76                 a.t -= (this.offset - rad);
77                 a.l += 1;
78                 a.w -= (this.offset + rad + 1);
79                 a.h -= (this.offset + rad);
80                 a.h += 1;
81             }
82         break;
83     };
84
85     this.adjusts = a;
86 };
87
88 Roo.Shadow.prototype = {
89     /**
90      * @cfg {String} mode
91      * The shadow display mode.  Supports the following options:<br />
92      * sides: Shadow displays on both sides and bottom only<br />
93      * frame: Shadow displays equally on all four sides<br />
94      * drop: Traditional bottom-right drop shadow (default)
95      */
96     /**
97      * @cfg {String} offset
98      * The number of pixels to offset the shadow from the element (defaults to 4)
99      */
100     offset: 4,
101
102     // private
103     defaultMode: "drop",
104
105     /**
106      * Displays the shadow under the target element
107      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
108      */
109     show : function(target){
110         target = Roo.get(target);
111         if(!this.el){
112             this.el = Roo.Shadow.Pool.pull();
113             if(this.el.dom.nextSibling != target.dom){
114                 this.el.insertBefore(target);
115             }
116         }
117         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
118         if(Roo.isIE){
119             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
120         }
121         this.realign(
122             target.getLeft(true),
123             target.getTop(true),
124             target.getWidth(),
125             target.getHeight()
126         );
127         this.el.dom.style.display = "block";
128     },
129
130     /**
131      * Returns true if the shadow is visible, else false
132      */
133     isVisible : function(){
134         return this.el ? true : false;  
135     },
136
137     /**
138      * Direct alignment when values are already available. Show must be called at least once before
139      * calling this method to ensure it is initialized.
140      * @param {Number} left The target element left position
141      * @param {Number} top The target element top position
142      * @param {Number} width The target element width
143      * @param {Number} height The target element height
144      */
145     realign : function(l, t, w, h){
146         if(!this.el){
147             return;
148         }
149         var a = this.adjusts, d = this.el.dom, s = d.style;
150         var iea = 0;
151         s.left = (l+a.l)+"px";
152         s.top = (t+a.t)+"px";
153         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
154  
155         if(s.width != sws || s.height != shs){
156             s.width = sws;
157             s.height = shs;
158             if(!Roo.isIE){
159                 var cn = d.childNodes;
160                 var sww = Math.max(0, (sw-12))+"px";
161                 cn[0].childNodes[1].style.width = sww;
162                 cn[1].childNodes[1].style.width = sww;
163                 cn[2].childNodes[1].style.width = sww;
164                 cn[1].style.height = Math.max(0, (sh-12))+"px";
165             }
166         }
167     },
168
169     /**
170      * Hides this shadow
171      */
172     hide : function(){
173         if(this.el){
174             this.el.dom.style.display = "none";
175             Roo.Shadow.Pool.push(this.el);
176             delete this.el;
177         }
178     },
179
180     /**
181      * Adjust the z-index of this shadow
182      * @param {Number} zindex The new z-index
183      */
184     setZIndex : function(z){
185         this.zIndex = z;
186         if(this.el){
187             this.el.setStyle("z-index", z);
188         }
189     }
190 };
191
192 // Private utility class that manages the internal Shadow cache
193 Roo.Shadow.Pool = function(){
194     var p = [];
195     var markup = Roo.isIE ?
196                  '<div class="x-ie-shadow"></div>' :
197                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
198     return {
199         pull : function(){
200             var sh = p.shift();
201             if(!sh){
202                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
203                 sh.autoBoxAdjust = false;
204             }
205             return sh;
206         },
207
208         push : function(sh){
209             p.push(sh);
210         }
211     };
212 }();/*
213  * - LGPL
214  *
215  * base class for bootstrap elements.
216  * 
217  */
218
219 Roo.bootstrap = Roo.bootstrap || {};
220 /**
221  * @class Roo.bootstrap.Component
222  * @extends Roo.Component
223  * Bootstrap Component base class
224  * @cfg {String} cls css class
225  * @cfg {String} style any extra css
226  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
227  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
228  * @cfg {string} dataId cutomer id
229  * @cfg {string} name Specifies name attribute
230  * @cfg {string} tooltip  Text for the tooltip
231  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
232  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
233  
234  * @constructor
235  * Do not use directly - it does not do anything..
236  * @param {Object} config The config object
237  */
238
239
240
241 Roo.bootstrap.Component = function(config){
242     Roo.bootstrap.Component.superclass.constructor.call(this, config);
243        
244     this.addEvents({
245         /**
246          * @event childrenrendered
247          * Fires when the children have been rendered..
248          * @param {Roo.bootstrap.Component} this
249          */
250         "childrenrendered" : true
251         
252         
253         
254     });
255     
256     
257 };
258
259 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
260     
261     
262     allowDomMove : false, // to stop relocations in parent onRender...
263     
264     cls : false,
265     
266     style : false,
267     
268     autoCreate : false,
269     
270     tooltip : null,
271     /**
272      * Initialize Events for the element
273      */
274     initEvents : function() { },
275     
276     xattr : false,
277     
278     parentId : false,
279     
280     can_build_overlaid : true,
281     
282     container_method : false,
283     
284     dataId : false,
285     
286     name : false,
287     
288     parent: function() {
289         // returns the parent component..
290         return Roo.ComponentMgr.get(this.parentId)
291         
292         
293     },
294     
295     // private
296     onRender : function(ct, position)
297     {
298        // Roo.log("Call onRender: " + this.xtype);
299         
300         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
301         
302         if(this.el){
303             if (this.el.attr('xtype')) {
304                 this.el.attr('xtypex', this.el.attr('xtype'));
305                 this.el.dom.removeAttribute('xtype');
306                 
307                 this.initEvents();
308             }
309             
310             return;
311         }
312         
313          
314         
315         var cfg = Roo.apply({},  this.getAutoCreate());
316         
317         cfg.id = this.id || Roo.id();
318         
319         // fill in the extra attributes 
320         if (this.xattr && typeof(this.xattr) =='object') {
321             for (var i in this.xattr) {
322                 cfg[i] = this.xattr[i];
323             }
324         }
325         
326         if(this.dataId){
327             cfg.dataId = this.dataId;
328         }
329         
330         if (this.cls) {
331             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
332         }
333         
334         if (this.style) { // fixme needs to support more complex style data.
335             cfg.style = this.style;
336         }
337         
338         if(this.name){
339             cfg.name = this.name;
340         }
341         
342         this.el = ct.createChild(cfg, position);
343         
344         if (this.tooltip) {
345             this.tooltipEl().attr('tooltip', this.tooltip);
346         }
347         
348         if(this.tabIndex !== undefined){
349             this.el.dom.setAttribute('tabIndex', this.tabIndex);
350         }
351         
352         this.initEvents();
353         
354     },
355     /**
356      * Fetch the element to add children to
357      * @return {Roo.Element} defaults to this.el
358      */
359     getChildContainer : function()
360     {
361         return this.el;
362     },
363     /**
364      * Fetch the element to display the tooltip on.
365      * @return {Roo.Element} defaults to this.el
366      */
367     tooltipEl : function()
368     {
369         return this.el;
370     },
371         
372     addxtype  : function(tree,cntr)
373     {
374         var cn = this;
375         
376         cn = Roo.factory(tree);
377         //Roo.log(['addxtype', cn]);
378            
379         cn.parentType = this.xtype; //??
380         cn.parentId = this.id;
381         
382         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
383         if (typeof(cn.container_method) == 'string') {
384             cntr = cn.container_method;
385         }
386         
387         
388         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
389         
390         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
391         
392         var build_from_html =  Roo.XComponent.build_from_html;
393           
394         var is_body  = (tree.xtype == 'Body') ;
395           
396         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
397           
398         var self_cntr_el = Roo.get(this[cntr](false));
399         
400         // do not try and build conditional elements 
401         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
402             return false;
403         }
404         
405         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
406             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
407                 return this.addxtypeChild(tree,cntr, is_body);
408             }
409             
410             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
411                 
412             if(echild){
413                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
414             }
415             
416             Roo.log('skipping render');
417             return cn;
418             
419         }
420         
421         var ret = false;
422         if (!build_from_html) {
423             return false;
424         }
425         
426         // this i think handles overlaying multiple children of the same type
427         // with the sam eelement.. - which might be buggy..
428         while (true) {
429             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
430             
431             if (!echild) {
432                 break;
433             }
434             
435             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
436                 break;
437             }
438             
439             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
440         }
441        
442         return ret;
443     },
444     
445     
446     addxtypeChild : function (tree, cntr, is_body)
447     {
448         Roo.debug && Roo.log('addxtypeChild:' + cntr);
449         var cn = this;
450         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
451         
452         
453         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
454                     (typeof(tree['flexy:foreach']) != 'undefined');
455           
456     
457         
458         skip_children = false;
459         // render the element if it's not BODY.
460         if (!is_body) {
461             
462             // if parent was disabled, then do not try and create the children..
463             if(!this[cntr](true)){
464                 tree.items = [];
465                 return tree;
466             }
467            
468             cn = Roo.factory(tree);
469            
470             cn.parentType = this.xtype; //??
471             cn.parentId = this.id;
472             
473             var build_from_html =  Roo.XComponent.build_from_html;
474             
475             
476             // does the container contain child eleemnts with 'xtype' attributes.
477             // that match this xtype..
478             // note - when we render we create these as well..
479             // so we should check to see if body has xtype set.
480             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
481                
482                 var self_cntr_el = Roo.get(this[cntr](false));
483                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
484                 if (echild) { 
485                     //Roo.log(Roo.XComponent.build_from_html);
486                     //Roo.log("got echild:");
487                     //Roo.log(echild);
488                 }
489                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
490                 // and are not displayed -this causes this to use up the wrong element when matching.
491                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
492                 
493                 
494                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
495                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
496                   
497                   
498                   
499                     cn.el = echild;
500                   //  Roo.log("GOT");
501                     //echild.dom.removeAttribute('xtype');
502                 } else {
503                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
504                     Roo.debug && Roo.log(self_cntr_el);
505                     Roo.debug && Roo.log(echild);
506                     Roo.debug && Roo.log(cn);
507                 }
508             }
509            
510             
511            
512             // if object has flexy:if - then it may or may not be rendered.
513             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
514                 // skip a flexy if element.
515                 Roo.debug && Roo.log('skipping render');
516                 Roo.debug && Roo.log(tree);
517                 if (!cn.el) {
518                     Roo.debug && Roo.log('skipping all children');
519                     skip_children = true;
520                 }
521                 
522              } else {
523                  
524                 // actually if flexy:foreach is found, we really want to create 
525                 // multiple copies here...
526                 //Roo.log('render');
527                 //Roo.log(this[cntr]());
528                 // some elements do not have render methods.. like the layouts...
529                 /*
530                 if(this[cntr](true) === false){
531                     cn.items = [];
532                     return cn;
533                 }
534                 */
535                 cn.render && cn.render(this[cntr](true));
536                 
537              }
538             // then add the element..
539         }
540          
541         // handle the kids..
542         
543         var nitems = [];
544         /*
545         if (typeof (tree.menu) != 'undefined') {
546             tree.menu.parentType = cn.xtype;
547             tree.menu.triggerEl = cn.el;
548             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
549             
550         }
551         */
552         if (!tree.items || !tree.items.length) {
553             cn.items = nitems;
554             //Roo.log(["no children", this]);
555             
556             return cn;
557         }
558          
559         var items = tree.items;
560         delete tree.items;
561         
562         //Roo.log(items.length);
563             // add the items..
564         if (!skip_children) {    
565             for(var i =0;i < items.length;i++) {
566               //  Roo.log(['add child', items[i]]);
567                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
568             }
569         }
570         
571         cn.items = nitems;
572         
573         //Roo.log("fire childrenrendered");
574         
575         cn.fireEvent('childrenrendered', this);
576         
577         return cn;
578     },
579     
580     /**
581      * Set the element that will be used to show or hide
582      */
583     setVisibilityEl : function(el)
584     {
585         this.visibilityEl = el;
586     },
587     
588      /**
589      * Get the element that will be used to show or hide
590      */
591     getVisibilityEl : function()
592     {
593         if (typeof(this.visibilityEl) == 'object') {
594             return this.visibilityEl;
595         }
596         
597         if (typeof(this.visibilityEl) == 'string') {
598             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
599         }
600         
601         return this.getEl();
602     },
603     
604     /**
605      * Show a component - removes 'hidden' class
606      */
607     show : function()
608     {
609         if(!this.getVisibilityEl()){
610             return;
611         }
612          
613         this.getVisibilityEl().removeClass(['hidden','d-none']);
614         
615         this.fireEvent('show', this);
616         
617         
618     },
619     /**
620      * Hide a component - adds 'hidden' class
621      */
622     hide: function()
623     {
624         if(!this.getVisibilityEl()){
625             return;
626         }
627         
628         this.getVisibilityEl().addClass(['hidden','d-none']);
629         
630         this.fireEvent('hide', this);
631         
632     }
633 });
634
635  /*
636  * - LGPL
637  *
638  * element
639  * 
640  */
641
642 /**
643  * @class Roo.bootstrap.Element
644  * @extends Roo.bootstrap.Component
645  * Bootstrap Element class
646  * @cfg {String} html contents of the element
647  * @cfg {String} tag tag of the element
648  * @cfg {String} cls class of the element
649  * @cfg {Boolean} preventDefault (true|false) default false
650  * @cfg {Boolean} clickable (true|false) default false
651  * 
652  * @constructor
653  * Create a new Element
654  * @param {Object} config The config object
655  */
656
657 Roo.bootstrap.Element = function(config){
658     Roo.bootstrap.Element.superclass.constructor.call(this, config);
659     
660     this.addEvents({
661         // raw events
662         /**
663          * @event click
664          * When a element is chick
665          * @param {Roo.bootstrap.Element} this
666          * @param {Roo.EventObject} e
667          */
668         "click" : true
669     });
670 };
671
672 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
673     
674     tag: 'div',
675     cls: '',
676     html: '',
677     preventDefault: false, 
678     clickable: false,
679     
680     getAutoCreate : function(){
681         
682         var cfg = {
683             tag: this.tag,
684             // cls: this.cls, double assign in parent class Component.js :: onRender
685             html: this.html
686         };
687         
688         return cfg;
689     },
690     
691     initEvents: function() 
692     {
693         Roo.bootstrap.Element.superclass.initEvents.call(this);
694         
695         if(this.clickable){
696             this.el.on('click', this.onClick, this);
697         }
698         
699     },
700     
701     onClick : function(e)
702     {
703         if(this.preventDefault){
704             e.preventDefault();
705         }
706         
707         this.fireEvent('click', this, e);
708     },
709     
710     getValue : function()
711     {
712         return this.el.dom.innerHTML;
713     },
714     
715     setValue : function(value)
716     {
717         this.el.dom.innerHTML = value;
718     }
719    
720 });
721
722  
723
724  /*
725  * - LGPL
726  *
727  * dropable area
728  * 
729  */
730
731 /**
732  * @class Roo.bootstrap.DropTarget
733  * @extends Roo.bootstrap.Element
734  * Bootstrap DropTarget class
735  
736  * @cfg {string} name dropable name
737  * 
738  * @constructor
739  * Create a new Dropable Area
740  * @param {Object} config The config object
741  */
742
743 Roo.bootstrap.DropTarget = function(config){
744     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
745     
746     this.addEvents({
747         // raw events
748         /**
749          * @event click
750          * When a element is chick
751          * @param {Roo.bootstrap.Element} this
752          * @param {Roo.EventObject} e
753          */
754         "drop" : true
755     });
756 };
757
758 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
759     
760     
761     getAutoCreate : function(){
762         
763          
764     },
765     
766     initEvents: function() 
767     {
768         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
769         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
770             ddGroup: this.name,
771             listeners : {
772                 drop : this.dragDrop.createDelegate(this),
773                 enter : this.dragEnter.createDelegate(this),
774                 out : this.dragOut.createDelegate(this),
775                 over : this.dragOver.createDelegate(this)
776             }
777             
778         });
779         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
780     },
781     
782     dragDrop : function(source,e,data)
783     {
784         // user has to decide how to impliment this.
785         Roo.log('drop');
786         Roo.log(this);
787         //this.fireEvent('drop', this, source, e ,data);
788         return false;
789     },
790     
791     dragEnter : function(n, dd, e, data)
792     {
793         // probably want to resize the element to match the dropped element..
794         Roo.log("enter");
795         this.originalSize = this.el.getSize();
796         this.el.setSize( n.el.getSize());
797         this.dropZone.DDM.refreshCache(this.name);
798         Roo.log([n, dd, e, data]);
799     },
800     
801     dragOut : function(value)
802     {
803         // resize back to normal
804         Roo.log("out");
805         this.el.setSize(this.originalSize);
806         this.dropZone.resetConstraints();
807     },
808     
809     dragOver : function()
810     {
811         // ??? do nothing?
812     }
813    
814 });
815
816  
817
818  /*
819  * - LGPL
820  *
821  * Body
822  *
823  */
824
825 /**
826  * @class Roo.bootstrap.Body
827  * @extends Roo.bootstrap.Component
828  * Bootstrap Body class
829  *
830  * @constructor
831  * Create a new body
832  * @param {Object} config The config object
833  */
834
835 Roo.bootstrap.Body = function(config){
836
837     config = config || {};
838
839     Roo.bootstrap.Body.superclass.constructor.call(this, config);
840     this.el = Roo.get(config.el ? config.el : document.body );
841     if (this.cls && this.cls.length) {
842         Roo.get(document.body).addClass(this.cls);
843     }
844 };
845
846 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
847
848     is_body : true,// just to make sure it's constructed?
849
850         autoCreate : {
851         cls: 'container'
852     },
853     onRender : function(ct, position)
854     {
855        /* Roo.log("Roo.bootstrap.Body - onRender");
856         if (this.cls && this.cls.length) {
857             Roo.get(document.body).addClass(this.cls);
858         }
859         // style??? xttr???
860         */
861     }
862
863
864
865
866 });
867 /*
868  * - LGPL
869  *
870  * button group
871  * 
872  */
873
874
875 /**
876  * @class Roo.bootstrap.ButtonGroup
877  * @extends Roo.bootstrap.Component
878  * Bootstrap ButtonGroup class
879  * @cfg {String} size lg | sm | xs (default empty normal)
880  * @cfg {String} align vertical | justified  (default none)
881  * @cfg {String} direction up | down (default down)
882  * @cfg {Boolean} toolbar false | true
883  * @cfg {Boolean} btn true | false
884  * 
885  * 
886  * @constructor
887  * Create a new Input
888  * @param {Object} config The config object
889  */
890
891 Roo.bootstrap.ButtonGroup = function(config){
892     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
893 };
894
895 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
896     
897     size: '',
898     align: '',
899     direction: '',
900     toolbar: false,
901     btn: true,
902
903     getAutoCreate : function(){
904         var cfg = {
905             cls: 'btn-group',
906             html : null
907         };
908         
909         cfg.html = this.html || cfg.html;
910         
911         if (this.toolbar) {
912             cfg = {
913                 cls: 'btn-toolbar',
914                 html: null
915             };
916             
917             return cfg;
918         }
919         
920         if (['vertical','justified'].indexOf(this.align)!==-1) {
921             cfg.cls = 'btn-group-' + this.align;
922             
923             if (this.align == 'justified') {
924                 console.log(this.items);
925             }
926         }
927         
928         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
929             cfg.cls += ' btn-group-' + this.size;
930         }
931         
932         if (this.direction == 'up') {
933             cfg.cls += ' dropup' ;
934         }
935         
936         return cfg;
937     },
938     /**
939      * Add a button to the group (similar to NavItem API.)
940      */
941     addItem : function(cfg)
942     {
943         var cn = new Roo.bootstrap.Button(cfg);
944         //this.register(cn);
945         cn.parentId = this.id;
946         cn.onRender(this.el, null);
947         return cn;
948     }
949    
950 });
951
952  /*
953  * - LGPL
954  *
955  * button
956  * 
957  */
958
959 /**
960  * @class Roo.bootstrap.Button
961  * @extends Roo.bootstrap.Component
962  * Bootstrap Button class
963  * @cfg {String} html The button content
964  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
965  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
966  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
967  * @cfg {String} size (lg|sm|xs)
968  * @cfg {String} tag (a|input|submit)
969  * @cfg {String} href empty or href
970  * @cfg {Boolean} disabled default false;
971  * @cfg {Boolean} isClose default false;
972  * @cfg {String} glyphicon depricated - use fa
973  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
974  * @cfg {String} badge text for badge
975  * @cfg {String} theme (default|glow)  
976  * @cfg {Boolean} inverse dark themed version
977  * @cfg {Boolean} toggle is it a slidy toggle button
978  * @cfg {Boolean} pressed   default null - if the button ahs active state
979  * @cfg {String} ontext text for on slidy toggle state
980  * @cfg {String} offtext text for off slidy toggle state
981  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
982  * @cfg {Boolean} removeClass remove the standard class..
983  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
984  * 
985  * @constructor
986  * Create a new button
987  * @param {Object} config The config object
988  */
989
990
991 Roo.bootstrap.Button = function(config){
992     Roo.bootstrap.Button.superclass.constructor.call(this, config);
993     
994     this.addEvents({
995         // raw events
996         /**
997          * @event click
998          * When a butotn is pressed
999          * @param {Roo.bootstrap.Button} btn
1000          * @param {Roo.EventObject} e
1001          */
1002         "click" : true,
1003          /**
1004          * @event toggle
1005          * After the button has been toggles
1006          * @param {Roo.bootstrap.Button} btn
1007          * @param {Roo.EventObject} e
1008          * @param {boolean} pressed (also available as button.pressed)
1009          */
1010         "toggle" : true
1011     });
1012 };
1013
1014 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1015     html: false,
1016     active: false,
1017     weight: '',
1018     badge_weight: '',
1019     outline : false,
1020     size: '',
1021     tag: 'button',
1022     href: '',
1023     disabled: false,
1024     isClose: false,
1025     glyphicon: '',
1026     fa: '',
1027     badge: '',
1028     theme: 'default',
1029     inverse: false,
1030     
1031     toggle: false,
1032     ontext: 'ON',
1033     offtext: 'OFF',
1034     defaulton: true,
1035     preventDefault: true,
1036     removeClass: false,
1037     name: false,
1038     target: false,
1039      
1040     pressed : null,
1041      
1042     
1043     getAutoCreate : function(){
1044         
1045         var cfg = {
1046             tag : 'button',
1047             cls : 'roo-button',
1048             html: ''
1049         };
1050         
1051         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1052             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1053             this.tag = 'button';
1054         } else {
1055             cfg.tag = this.tag;
1056         }
1057         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1058         
1059         if (this.toggle == true) {
1060             cfg={
1061                 tag: 'div',
1062                 cls: 'slider-frame roo-button',
1063                 cn: [
1064                     {
1065                         tag: 'span',
1066                         'data-on-text':'ON',
1067                         'data-off-text':'OFF',
1068                         cls: 'slider-button',
1069                         html: this.offtext
1070                     }
1071                 ]
1072             };
1073             
1074             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
1075                 cfg.cls += ' '+this.weight;
1076             }
1077             
1078             return cfg;
1079         }
1080         
1081         if (this.isClose) {
1082             cfg.cls += ' close';
1083             
1084             cfg["aria-hidden"] = true;
1085             
1086             cfg.html = "&times;";
1087             
1088             return cfg;
1089         }
1090              
1091         
1092         if (this.theme==='default') {
1093             cfg.cls = 'btn roo-button';
1094             
1095             //if (this.parentType != 'Navbar') {
1096             this.weight = this.weight.length ?  this.weight : 'default';
1097             //}
1098             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
1099                 
1100                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1101                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1102                 cfg.cls += ' btn-' + outline + weight;
1103                 if (this.weight == 'default') {
1104                     // BC
1105                     cfg.cls += ' btn-' + this.weight;
1106                 }
1107             }
1108         } else if (this.theme==='glow') {
1109             
1110             cfg.tag = 'a';
1111             cfg.cls = 'btn-glow roo-button';
1112             
1113             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
1114                 
1115                 cfg.cls += ' ' + this.weight;
1116             }
1117         }
1118    
1119         
1120         if (this.inverse) {
1121             this.cls += ' inverse';
1122         }
1123         
1124         
1125         if (this.active || this.pressed === true) {
1126             cfg.cls += ' active';
1127         }
1128         
1129         if (this.disabled) {
1130             cfg.disabled = 'disabled';
1131         }
1132         
1133         if (this.items) {
1134             Roo.log('changing to ul' );
1135             cfg.tag = 'ul';
1136             this.glyphicon = 'caret';
1137             if (Roo.bootstrap.version == 4) {
1138                 this.fa = 'caret-down';
1139             }
1140             
1141         }
1142         
1143         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1144          
1145         //gsRoo.log(this.parentType);
1146         if (this.parentType === 'Navbar' && !this.parent().bar) {
1147             Roo.log('changing to li?');
1148             
1149             cfg.tag = 'li';
1150             
1151             cfg.cls = '';
1152             cfg.cn =  [{
1153                 tag : 'a',
1154                 cls : 'roo-button',
1155                 html : this.html,
1156                 href : this.href || '#'
1157             }];
1158             if (this.menu) {
1159                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1160                 cfg.cls += ' dropdown';
1161             }   
1162             
1163             delete cfg.html;
1164             
1165         }
1166         
1167        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1168         
1169         if (this.glyphicon) {
1170             cfg.html = ' ' + cfg.html;
1171             
1172             cfg.cn = [
1173                 {
1174                     tag: 'span',
1175                     cls: 'glyphicon glyphicon-' + this.glyphicon
1176                 }
1177             ];
1178         }
1179         if (this.fa) {
1180             cfg.html = ' ' + cfg.html;
1181             
1182             cfg.cn = [
1183                 {
1184                     tag: 'i',
1185                     cls: 'fa fas fa-' + this.fa
1186                 }
1187             ];
1188         }
1189         
1190         if (this.badge) {
1191             cfg.html += ' ';
1192             
1193             cfg.tag = 'a';
1194             
1195 //            cfg.cls='btn roo-button';
1196             
1197             cfg.href=this.href;
1198             
1199             var value = cfg.html;
1200             
1201             if(this.glyphicon){
1202                 value = {
1203                     tag: 'span',
1204                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1205                     html: this.html
1206                 };
1207             }
1208             if(this.fa){
1209                 value = {
1210                     tag: 'i',
1211                     cls: 'fa fas fa-' + this.fa,
1212                     html: this.html
1213                 };
1214             }
1215             
1216             var bw = this.badge_weight.length ? this.badge_weight :
1217                 (this.weight.length ? this.weight : 'secondary');
1218             bw = bw == 'default' ? 'secondary' : bw;
1219             
1220             cfg.cn = [
1221                 value,
1222                 {
1223                     tag: 'span',
1224                     cls: 'badge badge-' + bw,
1225                     html: this.badge
1226                 }
1227             ];
1228             
1229             cfg.html='';
1230         }
1231         
1232         if (this.menu) {
1233             cfg.cls += ' dropdown';
1234             cfg.html = typeof(cfg.html) != 'undefined' ?
1235                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1236         }
1237         
1238         if (cfg.tag !== 'a' && this.href !== '') {
1239             throw "Tag must be a to set href.";
1240         } else if (this.href.length > 0) {
1241             cfg.href = this.href;
1242         }
1243         
1244         if(this.removeClass){
1245             cfg.cls = '';
1246         }
1247         
1248         if(this.target){
1249             cfg.target = this.target;
1250         }
1251         
1252         return cfg;
1253     },
1254     initEvents: function() {
1255        // Roo.log('init events?');
1256 //        Roo.log(this.el.dom);
1257         // add the menu...
1258         
1259         if (typeof (this.menu) != 'undefined') {
1260             this.menu.parentType = this.xtype;
1261             this.menu.triggerEl = this.el;
1262             this.addxtype(Roo.apply({}, this.menu));
1263         }
1264
1265
1266        if (this.el.hasClass('roo-button')) {
1267             this.el.on('click', this.onClick, this);
1268        } else {
1269             this.el.select('.roo-button').on('click', this.onClick, this);
1270        }
1271        
1272        if(this.removeClass){
1273            this.el.on('click', this.onClick, this);
1274        }
1275        
1276        this.el.enableDisplayMode();
1277         
1278     },
1279     onClick : function(e)
1280     {
1281         if (this.disabled) {
1282             return;
1283         }
1284         
1285         Roo.log('button on click ');
1286         if(this.preventDefault){
1287             e.preventDefault();
1288         }
1289         
1290         if (this.pressed === true || this.pressed === false) {
1291             this.toggleActive(e);
1292         }
1293         
1294         
1295         this.fireEvent('click', this, e);
1296     },
1297     
1298     /**
1299      * Enables this button
1300      */
1301     enable : function()
1302     {
1303         this.disabled = false;
1304         this.el.removeClass('disabled');
1305     },
1306     
1307     /**
1308      * Disable this button
1309      */
1310     disable : function()
1311     {
1312         this.disabled = true;
1313         this.el.addClass('disabled');
1314     },
1315      /**
1316      * sets the active state on/off, 
1317      * @param {Boolean} state (optional) Force a particular state
1318      */
1319     setActive : function(v) {
1320         
1321         this.el[v ? 'addClass' : 'removeClass']('active');
1322         this.pressed = v;
1323     },
1324      /**
1325      * toggles the current active state 
1326      */
1327     toggleActive : function(e)
1328     {
1329         this.setActive(!this.pressed);
1330         this.fireEvent('toggle', this, e, !this.pressed);
1331     },
1332      /**
1333      * get the current active state
1334      * @return {boolean} true if it's active
1335      */
1336     isActive : function()
1337     {
1338         return this.el.hasClass('active');
1339     },
1340     /**
1341      * set the text of the first selected button
1342      */
1343     setText : function(str)
1344     {
1345         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1346     },
1347     /**
1348      * get the text of the first selected button
1349      */
1350     getText : function()
1351     {
1352         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1353     },
1354     
1355     setWeight : function(str)
1356     {
1357         this.el.removeClass(Roo.bootstrap.Button.weightClass );
1358         this.weight = str;
1359         var outline = this.outline ? 'outline-' : '';
1360         if (str == 'default') {
1361             this.el.addClass('btn-default btn-outline-secondary');        
1362             return;
1363         }
1364         this.el.addClass('btn-' + outline + str);        
1365     }
1366     
1367     
1368 });
1369 Roo.bootstrap.Button.weightClass = [
1370                         
1371        "btn-default",
1372        "btn-outline-secondary",
1373        "btn-secondary",        
1374        "btn-primary", 
1375        "btn-success", 
1376        "btn-info", 
1377        "btn-warning",
1378        "btn-danger",
1379        "btn-link",
1380        'btn-light',
1381        'btn-dark'
1382 ];/*
1383  * - LGPL
1384  *
1385  * column
1386  * 
1387  */
1388
1389 /**
1390  * @class Roo.bootstrap.Column
1391  * @extends Roo.bootstrap.Component
1392  * Bootstrap Column class
1393  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1394  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1395  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1396  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1397  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1398  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1399  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1400  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1401  *
1402  * 
1403  * @cfg {Boolean} hidden (true|false) hide the element
1404  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1405  * @cfg {String} fa (ban|check|...) font awesome icon
1406  * @cfg {Number} fasize (1|2|....) font awsome size
1407
1408  * @cfg {String} icon (info-sign|check|...) glyphicon name
1409
1410  * @cfg {String} html content of column.
1411  * 
1412  * @constructor
1413  * Create a new Column
1414  * @param {Object} config The config object
1415  */
1416
1417 Roo.bootstrap.Column = function(config){
1418     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1419 };
1420
1421 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1422     
1423     xs: false,
1424     sm: false,
1425     md: false,
1426     lg: false,
1427     xsoff: false,
1428     smoff: false,
1429     mdoff: false,
1430     lgoff: false,
1431     html: '',
1432     offset: 0,
1433     alert: false,
1434     fa: false,
1435     icon : false,
1436     hidden : false,
1437     fasize : 1,
1438     
1439     getAutoCreate : function(){
1440         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1441         
1442         cfg = {
1443             tag: 'div',
1444             cls: 'column'
1445         };
1446         
1447         var settings=this;
1448         var sizes =   ['xs','sm','md','lg'];
1449         sizes.map(function(size ,ix){
1450             //Roo.log( size + ':' + settings[size]);
1451             
1452             if (settings[size+'off'] !== false) {
1453                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1454             }
1455             
1456             if (settings[size] === false) {
1457                 return;
1458             }
1459             
1460             if (!settings[size]) { // 0 = hidden
1461                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1462                 // bootsrap4
1463                 for (var i = ix; i > -1; i--) {
1464                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1465                 }
1466                 
1467                 
1468                 return;
1469             }
1470             cfg.cls += ' col-' + size + '-' + settings[size] + (
1471                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1472             );
1473             
1474         });
1475         
1476         if (this.hidden) {
1477             cfg.cls += ' hidden';
1478         }
1479         
1480         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1481             cfg.cls +=' alert alert-' + this.alert;
1482         }
1483         
1484         
1485         if (this.html.length) {
1486             cfg.html = this.html;
1487         }
1488         if (this.fa) {
1489             var fasize = '';
1490             if (this.fasize > 1) {
1491                 fasize = ' fa-' + this.fasize + 'x';
1492             }
1493             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1494             
1495             
1496         }
1497         if (this.icon) {
1498             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1499         }
1500         
1501         return cfg;
1502     }
1503    
1504 });
1505
1506  
1507
1508  /*
1509  * - LGPL
1510  *
1511  * page container.
1512  * 
1513  */
1514
1515
1516 /**
1517  * @class Roo.bootstrap.Container
1518  * @extends Roo.bootstrap.Component
1519  * Bootstrap Container class
1520  * @cfg {Boolean} jumbotron is it a jumbotron element
1521  * @cfg {String} html content of element
1522  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1523  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1524  * @cfg {String} header content of header (for panel)
1525  * @cfg {String} footer content of footer (for panel)
1526  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1527  * @cfg {String} tag (header|aside|section) type of HTML tag.
1528  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1529  * @cfg {String} fa font awesome icon
1530  * @cfg {String} icon (info-sign|check|...) glyphicon name
1531  * @cfg {Boolean} hidden (true|false) hide the element
1532  * @cfg {Boolean} expandable (true|false) default false
1533  * @cfg {Boolean} expanded (true|false) default true
1534  * @cfg {String} rheader contet on the right of header
1535  * @cfg {Boolean} clickable (true|false) default false
1536
1537  *     
1538  * @constructor
1539  * Create a new Container
1540  * @param {Object} config The config object
1541  */
1542
1543 Roo.bootstrap.Container = function(config){
1544     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1545     
1546     this.addEvents({
1547         // raw events
1548          /**
1549          * @event expand
1550          * After the panel has been expand
1551          * 
1552          * @param {Roo.bootstrap.Container} this
1553          */
1554         "expand" : true,
1555         /**
1556          * @event collapse
1557          * After the panel has been collapsed
1558          * 
1559          * @param {Roo.bootstrap.Container} this
1560          */
1561         "collapse" : true,
1562         /**
1563          * @event click
1564          * When a element is chick
1565          * @param {Roo.bootstrap.Container} this
1566          * @param {Roo.EventObject} e
1567          */
1568         "click" : true
1569     });
1570 };
1571
1572 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1573     
1574     jumbotron : false,
1575     well: '',
1576     panel : '',
1577     header: '',
1578     footer : '',
1579     sticky: '',
1580     tag : false,
1581     alert : false,
1582     fa: false,
1583     icon : false,
1584     expandable : false,
1585     rheader : '',
1586     expanded : true,
1587     clickable: false,
1588   
1589      
1590     getChildContainer : function() {
1591         
1592         if(!this.el){
1593             return false;
1594         }
1595         
1596         if (this.panel.length) {
1597             return this.el.select('.panel-body',true).first();
1598         }
1599         
1600         return this.el;
1601     },
1602     
1603     
1604     getAutoCreate : function(){
1605         
1606         var cfg = {
1607             tag : this.tag || 'div',
1608             html : '',
1609             cls : ''
1610         };
1611         if (this.jumbotron) {
1612             cfg.cls = 'jumbotron';
1613         }
1614         
1615         
1616         
1617         // - this is applied by the parent..
1618         //if (this.cls) {
1619         //    cfg.cls = this.cls + '';
1620         //}
1621         
1622         if (this.sticky.length) {
1623             
1624             var bd = Roo.get(document.body);
1625             if (!bd.hasClass('bootstrap-sticky')) {
1626                 bd.addClass('bootstrap-sticky');
1627                 Roo.select('html',true).setStyle('height', '100%');
1628             }
1629              
1630             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1631         }
1632         
1633         
1634         if (this.well.length) {
1635             switch (this.well) {
1636                 case 'lg':
1637                 case 'sm':
1638                     cfg.cls +=' well well-' +this.well;
1639                     break;
1640                 default:
1641                     cfg.cls +=' well';
1642                     break;
1643             }
1644         }
1645         
1646         if (this.hidden) {
1647             cfg.cls += ' hidden';
1648         }
1649         
1650         
1651         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1652             cfg.cls +=' alert alert-' + this.alert;
1653         }
1654         
1655         var body = cfg;
1656         
1657         if (this.panel.length) {
1658             cfg.cls += ' panel panel-' + this.panel;
1659             cfg.cn = [];
1660             if (this.header.length) {
1661                 
1662                 var h = [];
1663                 
1664                 if(this.expandable){
1665                     
1666                     cfg.cls = cfg.cls + ' expandable';
1667                     
1668                     h.push({
1669                         tag: 'i',
1670                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1671                     });
1672                     
1673                 }
1674                 
1675                 h.push(
1676                     {
1677                         tag: 'span',
1678                         cls : 'panel-title',
1679                         html : (this.expandable ? '&nbsp;' : '') + this.header
1680                     },
1681                     {
1682                         tag: 'span',
1683                         cls: 'panel-header-right',
1684                         html: this.rheader
1685                     }
1686                 );
1687                 
1688                 cfg.cn.push({
1689                     cls : 'panel-heading',
1690                     style : this.expandable ? 'cursor: pointer' : '',
1691                     cn : h
1692                 });
1693                 
1694             }
1695             
1696             body = false;
1697             cfg.cn.push({
1698                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1699                 html : this.html
1700             });
1701             
1702             
1703             if (this.footer.length) {
1704                 cfg.cn.push({
1705                     cls : 'panel-footer',
1706                     html : this.footer
1707                     
1708                 });
1709             }
1710             
1711         }
1712         
1713         if (body) {
1714             body.html = this.html || cfg.html;
1715             // prefix with the icons..
1716             if (this.fa) {
1717                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1718             }
1719             if (this.icon) {
1720                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1721             }
1722             
1723             
1724         }
1725         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1726             cfg.cls =  'container';
1727         }
1728         
1729         return cfg;
1730     },
1731     
1732     initEvents: function() 
1733     {
1734         if(this.expandable){
1735             var headerEl = this.headerEl();
1736         
1737             if(headerEl){
1738                 headerEl.on('click', this.onToggleClick, this);
1739             }
1740         }
1741         
1742         if(this.clickable){
1743             this.el.on('click', this.onClick, this);
1744         }
1745         
1746     },
1747     
1748     onToggleClick : function()
1749     {
1750         var headerEl = this.headerEl();
1751         
1752         if(!headerEl){
1753             return;
1754         }
1755         
1756         if(this.expanded){
1757             this.collapse();
1758             return;
1759         }
1760         
1761         this.expand();
1762     },
1763     
1764     expand : function()
1765     {
1766         if(this.fireEvent('expand', this)) {
1767             
1768             this.expanded = true;
1769             
1770             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1771             
1772             this.el.select('.panel-body',true).first().removeClass('hide');
1773             
1774             var toggleEl = this.toggleEl();
1775
1776             if(!toggleEl){
1777                 return;
1778             }
1779
1780             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1781         }
1782         
1783     },
1784     
1785     collapse : function()
1786     {
1787         if(this.fireEvent('collapse', this)) {
1788             
1789             this.expanded = false;
1790             
1791             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1792             this.el.select('.panel-body',true).first().addClass('hide');
1793         
1794             var toggleEl = this.toggleEl();
1795
1796             if(!toggleEl){
1797                 return;
1798             }
1799
1800             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1801         }
1802     },
1803     
1804     toggleEl : function()
1805     {
1806         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1807             return;
1808         }
1809         
1810         return this.el.select('.panel-heading .fa',true).first();
1811     },
1812     
1813     headerEl : function()
1814     {
1815         if(!this.el || !this.panel.length || !this.header.length){
1816             return;
1817         }
1818         
1819         return this.el.select('.panel-heading',true).first()
1820     },
1821     
1822     bodyEl : function()
1823     {
1824         if(!this.el || !this.panel.length){
1825             return;
1826         }
1827         
1828         return this.el.select('.panel-body',true).first()
1829     },
1830     
1831     titleEl : function()
1832     {
1833         if(!this.el || !this.panel.length || !this.header.length){
1834             return;
1835         }
1836         
1837         return this.el.select('.panel-title',true).first();
1838     },
1839     
1840     setTitle : function(v)
1841     {
1842         var titleEl = this.titleEl();
1843         
1844         if(!titleEl){
1845             return;
1846         }
1847         
1848         titleEl.dom.innerHTML = v;
1849     },
1850     
1851     getTitle : function()
1852     {
1853         
1854         var titleEl = this.titleEl();
1855         
1856         if(!titleEl){
1857             return '';
1858         }
1859         
1860         return titleEl.dom.innerHTML;
1861     },
1862     
1863     setRightTitle : function(v)
1864     {
1865         var t = this.el.select('.panel-header-right',true).first();
1866         
1867         if(!t){
1868             return;
1869         }
1870         
1871         t.dom.innerHTML = v;
1872     },
1873     
1874     onClick : function(e)
1875     {
1876         e.preventDefault();
1877         
1878         this.fireEvent('click', this, e);
1879     }
1880 });
1881
1882  /*
1883  *  - LGPL
1884  *
1885  *  This is BS4's Card element.. - similar to our containers probably..
1886  * 
1887  */
1888 /**
1889  * @class Roo.bootstrap.Card
1890  * @extends Roo.bootstrap.Component
1891  * Bootstrap Card class
1892  *
1893  *
1894  * possible... may not be implemented..
1895  * @cfg {String} header_image  src url of image.
1896  * @cfg {String|Object} header
1897  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1898  * 
1899  * @cfg {String} title
1900  * @cfg {String} subtitle
1901  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1902  * @cfg {String} footer
1903  
1904  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1905  * 
1906  * @cfg {String} margin (0|1|2|3|4|5|auto)
1907  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1908  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1909  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1910  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1911  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1912  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1913  *
1914  * @cfg {String} padding (0|1|2|3|4|5)
1915  * @cfg {String} padding_top (0|1|2|3|4|5)
1916  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1917  * @cfg {String} padding_left (0|1|2|3|4|5)
1918  * @cfg {String} padding_right (0|1|2|3|4|5)
1919  * @cfg {String} padding_x (0|1|2|3|4|5)
1920  * @cfg {String} padding_y (0|1|2|3|4|5)
1921  *
1922  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1923  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1924  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1925  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1926  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1927  
1928  * @config {Boolean} dragable  if this card can be dragged.
1929  * @config {String} drag_group  group for drag
1930  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1931  * @config {String} drop_group  group for drag
1932  * 
1933  * @config {Boolean} collapsable can the body be collapsed.
1934  * @config {Boolean} collapsed is the body collapsed when rendered...
1935  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1936  * @config {Boolean} rotated is the body rotated when rendered...
1937  * 
1938  * @constructor
1939  * Create a new Container
1940  * @param {Object} config The config object
1941  */
1942
1943 Roo.bootstrap.Card = function(config){
1944     Roo.bootstrap.Card.superclass.constructor.call(this, config);
1945     
1946     this.addEvents({
1947          // raw events
1948         /**
1949          * @event drop
1950          * When a element a card is dropped
1951          * @param {Roo.bootstrap.Element} this
1952          * @param {Roo.Element} n the node being dropped?
1953          * @param {Object} dd Drag and drop data
1954          * @param {Roo.EventObject} e
1955          * @param {Roo.EventObject} data  the data passed via getDragData
1956          */
1957         'drop' : true,
1958          /**
1959          * @event rotate
1960          * When a element a card is rotate
1961          * @param {Roo.bootstrap.Element} this
1962          * @param {Roo.Element} n the node being dropped?
1963          * @param {Boolean} rotate status
1964          */
1965         'rotate' : true
1966         
1967     });
1968 };
1969
1970
1971 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
1972     
1973     
1974     weight : '',
1975     
1976     margin: '', /// may be better in component?
1977     margin_top: '', 
1978     margin_bottom: '', 
1979     margin_left: '',
1980     margin_right: '',
1981     margin_x: '',
1982     margin_y: '',
1983     
1984     padding : '',
1985     padding_top: '', 
1986     padding_bottom: '', 
1987     padding_left: '',
1988     padding_right: '',
1989     padding_x: '',
1990     padding_y: '',
1991     
1992     display: '', 
1993     display_xs: '', 
1994     display_sm: '', 
1995     display_lg: '',
1996     display_xl: '',
1997  
1998     header_image  : '',
1999     header : '',
2000     header_size : 0,
2001     title : '',
2002     subtitle : '',
2003     html : '',
2004     footer: '',
2005
2006     collapsable : false,
2007     collapsed : false,
2008     rotateable : false,
2009     rotated : false,
2010     
2011     dragable : false,
2012     drag_group : false,
2013     dropable : false,
2014     drop_group : false,
2015     childContainer : false,
2016     dropEl : false, /// the dom placeholde element that indicates drop location.
2017     
2018     layoutCls : function()
2019     {
2020         var cls = '';
2021         var t = this;
2022         Roo.log(this.margin_bottom.length);
2023         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2024             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2025             
2026             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2027                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2028             }
2029             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2030                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2031             }
2032         });
2033         
2034         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2035             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2036                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
2037             }
2038         });
2039         
2040         // more generic support?
2041         if (this.hidden) {
2042             cls += ' d-none';
2043         }
2044         
2045         return cls;
2046     },
2047  
2048        // Roo.log("Call onRender: " + this.xtype);
2049         /*  We are looking at something like this.
2050 <div class="card">
2051     <img src="..." class="card-img-top" alt="...">
2052     <div class="card-body">
2053         <h5 class="card-title">Card title</h5>
2054          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2055
2056         >> this bit is really the body...
2057         <div> << we will ad dthis in hopefully it will not break shit.
2058         
2059         ** card text does not actually have any styling...
2060         
2061             <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>
2062         
2063         </div> <<
2064           <a href="#" class="card-link">Card link</a>
2065           
2066     </div>
2067     <div class="card-footer">
2068         <small class="text-muted">Last updated 3 mins ago</small>
2069     </div>
2070 </div>
2071          */
2072     getAutoCreate : function(){
2073         
2074         var cfg = {
2075             tag : 'div',
2076             cls : 'card',
2077             cn : [ ]
2078         };
2079         
2080         if (this.weight.length && this.weight != 'light') {
2081             cfg.cls += ' text-white';
2082         } else {
2083             cfg.cls += ' text-dark'; // need as it's nested..
2084         }
2085         if (this.weight.length) {
2086             cfg.cls += ' bg-' + this.weight;
2087         }
2088         
2089         cfg.cls += this.layoutCls(); 
2090         
2091         var hdr = false;
2092         if (this.header.length) {
2093             hdr = {
2094                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2095                 cls : 'card-header',
2096                 cn : []
2097             };
2098             cfg.cn.push(hdr);
2099             hdr_ctr = hdr;
2100         } else {
2101             hdr = {
2102                 tag : 'div',
2103                 cls : 'card-header d-none',
2104                 cn : []
2105             };
2106             cfg.cn.push(hdr);
2107         }
2108         if (this.collapsable) {
2109             hdr_ctr = {
2110             tag : 'a',
2111             cls : 'd-block user-select-none',
2112             cn: [
2113                     {
2114                         tag: 'i',
2115                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2116                     }
2117                    
2118                 ]
2119             };
2120             hdr.cn.push(hdr_ctr);
2121         }
2122         
2123         hdr_ctr.cn.push(        {
2124             tag: 'span',
2125             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2126             html : this.header
2127         });
2128         
2129         
2130         if (this.header_image.length) {
2131             cfg.cn.push({
2132                 tag : 'img',
2133                 cls : 'card-img-top',
2134                 src: this.header_image // escape?
2135             });
2136         } else {
2137             cfg.cn.push({
2138                     tag : 'div',
2139                     cls : 'card-img-top d-none' 
2140                 });
2141         }
2142             
2143         var body = {
2144             tag : 'div',
2145             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2146             cn : []
2147         };
2148         var obody = body;
2149         if (this.collapsable || this.rotateable) {
2150             obody = {
2151                 tag: 'div',
2152                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2153                 cn : [  body ]
2154             };
2155         }
2156         
2157         cfg.cn.push(obody);
2158         
2159         if (this.title.length) {
2160             body.cn.push({
2161                 tag : 'div',
2162                 cls : 'card-title',
2163                 src: this.title // escape?
2164             });
2165         }  
2166         
2167         if (this.subtitle.length) {
2168             body.cn.push({
2169                 tag : 'div',
2170                 cls : 'card-title',
2171                 src: this.subtitle // escape?
2172             });
2173         }
2174         
2175         body.cn.push({
2176             tag : 'div',
2177             cls : 'roo-card-body-ctr'
2178         });
2179         
2180         if (this.html.length) {
2181             body.cn.push({
2182                 tag: 'div',
2183                 html : this.html
2184             });
2185         }
2186         // fixme ? handle objects?
2187         
2188         if (this.footer.length) {
2189            
2190             cfg.cn.push({
2191                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2192                 html : this.footer
2193             });
2194             
2195         } else {
2196             cfg.cn.push({cls : 'card-footer d-none'});
2197         }
2198         
2199         // footer...
2200         
2201         return cfg;
2202     },
2203     
2204     
2205     getCardHeader : function()
2206     {
2207         var  ret = this.el.select('.card-header',true).first();
2208         if (ret.hasClass('d-none')) {
2209             ret.removeClass('d-none');
2210         }
2211         
2212         return ret;
2213     },
2214     getCardFooter : function()
2215     {
2216         var  ret = this.el.select('.card-footer',true).first();
2217         if (ret.hasClass('d-none')) {
2218             ret.removeClass('d-none');
2219         }
2220         
2221         return ret;
2222     },
2223     getCardImageTop : function()
2224     {
2225         var  ret = this.el.select('.card-img-top',true).first();
2226         if (ret.hasClass('d-none')) {
2227             ret.removeClass('d-none');
2228         }
2229             
2230         return ret;
2231     },
2232     
2233     getChildContainer : function()
2234     {
2235         
2236         if(!this.el){
2237             return false;
2238         }
2239         return this.el.select('.roo-card-body-ctr',true).first();    
2240     },
2241     
2242     initEvents: function() 
2243     {
2244         
2245         this.bodyEl = this.getChildContainer();
2246         if(this.dragable){
2247             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2248                     containerScroll: true,
2249                     ddGroup: this.drag_group || 'default_card_drag_group'
2250             });
2251             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2252         }
2253         if (this.dropable) {
2254             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2255                 containerScroll: true,
2256                 ddGroup: this.drop_group || 'default_card_drag_group'
2257             });
2258             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2259             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2260             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2261             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2262             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2263         }
2264         
2265         if (this.collapsable) {
2266             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2267         }
2268         if (this.rotateable) {
2269             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2270         }
2271         this.collapsableEl = this.el.select('.roo-collapsable').first();
2272          
2273         this.footerEl = this.el.select('.card-footer').first();
2274         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2275         this.headerEl = this.el.select('.roo-card-header-ctr').first();
2276         
2277         if (this.rotated) {
2278             this.el.addClass('roo-card-rotated');
2279             this.fireEvent('rotate', this, true);
2280         }
2281         
2282     },
2283     getDragData : function(e)
2284     {
2285         var target = this.getEl();
2286         if (target) {
2287             //this.handleSelection(e);
2288             
2289             var dragData = {
2290                 source: this,
2291                 copy: false,
2292                 nodes: this.getEl(),
2293                 records: []
2294             };
2295             
2296             
2297             dragData.ddel = target.dom ;    // the div element
2298             Roo.log(target.getWidth( ));
2299             dragData.ddel.style.width = target.getWidth() + 'px';
2300             
2301             return dragData;
2302         }
2303         return false;
2304     },
2305     /**
2306     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2307     *    whole Element becomes the target, and this causes the drop gesture to append.
2308     */
2309     getTargetFromEvent : function(e, dragged_card_el)
2310     {
2311         var target = e.getTarget();
2312         while ((target !== null) && (target.parentNode != this.bodyEl.dom)) {
2313             target = target.parentNode;
2314         }
2315         
2316         var ret = {
2317             position: '',
2318             cards : [],
2319             card_n : -1,
2320             items_n : -1,
2321             card : false 
2322         };
2323         
2324         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2325         // see if target is one of the 'cards'...
2326         
2327         
2328         //Roo.log(this.items.length);
2329         var pos = false;
2330         
2331         var last_card_n = 0;
2332         var cards_len  = 0;
2333         for (var i = 0;i< this.items.length;i++) {
2334             
2335             if (!this.items[i].el.hasClass('card')) {
2336                  continue;
2337             }
2338             pos = this.getDropPoint(e, this.items[i].el.dom);
2339             
2340             cards_len = ret.cards.length;
2341             //Roo.log(this.items[i].el.dom.id);
2342             ret.cards.push(this.items[i]);
2343             last_card_n  = i;
2344             if (ret.card_n < 0 && pos == 'above') {
2345                 ret.position = cards_len > 0 ? 'below' : pos;
2346                 ret.items_n = i > 0 ? i - 1 : 0;
2347                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2348                 ret.card = ret.cards[ret.card_n];
2349             }
2350         }
2351         if (!ret.cards.length) {
2352             ret.card = true;
2353             ret.position = 'below';
2354             ret.items_n;
2355             return ret;
2356         }
2357         // could not find a card.. stick it at the end..
2358         if (ret.card_n < 0) {
2359             ret.card_n = last_card_n;
2360             ret.card = ret.cards[last_card_n];
2361             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2362             ret.position = 'below';
2363         }
2364         
2365         if (this.items[ret.items_n].el == dragged_card_el) {
2366             return false;
2367         }
2368         
2369         if (ret.position == 'below') {
2370             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2371             
2372             if (card_after  && card_after.el == dragged_card_el) {
2373                 return false;
2374             }
2375             return ret;
2376         }
2377         
2378         // its's after ..
2379         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2380         
2381         if (card_before  && card_before.el == dragged_card_el) {
2382             return false;
2383         }
2384         
2385         return ret;
2386     },
2387     
2388     onNodeEnter : function(n, dd, e, data){
2389         return false;
2390     },
2391     onNodeOver : function(n, dd, e, data)
2392     {
2393        
2394         var target_info = this.getTargetFromEvent(e,data.source.el);
2395         if (target_info === false) {
2396             this.dropPlaceHolder('hide');
2397             return false;
2398         }
2399         Roo.log(['getTargetFromEvent', target_info ]);
2400         
2401          
2402         this.dropPlaceHolder('show', target_info,data);
2403         
2404         return false; 
2405     },
2406     onNodeOut : function(n, dd, e, data){
2407         this.dropPlaceHolder('hide');
2408      
2409     },
2410     onNodeDrop : function(n, dd, e, data)
2411     {
2412         
2413         // call drop - return false if
2414         
2415         // this could actually fail - if the Network drops..
2416         // we will ignore this at present..- client should probably reload
2417         // the whole set of cards if stuff like that fails.
2418         
2419         
2420         var info = this.getTargetFromEvent(e,data.source.el);
2421         if (info === false) {
2422             return false;
2423         }
2424         
2425         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
2426             return false;
2427         }
2428          
2429         this.dropPlaceHolder('hide');
2430         
2431         // do the dom manipulation first..
2432         var dom = data.source.el.dom;
2433         dom.parentNode.removeChild(dom);
2434         
2435         
2436         if (info.card !== true) {
2437             var cardel = info.card.el.dom;
2438             
2439             if (info.position == 'above') {
2440                 cardel.parentNode.insertBefore(dom, cardel);
2441             } else if (cardel.nextSibling) {
2442                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2443             } else {
2444                 cardel.parentNode.append(dom);
2445             }
2446         } else {
2447             // card container???
2448             this.bodyEl.dom.append(dom);
2449         }
2450         
2451         //FIXME HANDLE card = true 
2452         
2453         // add this to the correct place in items.
2454         
2455         
2456         
2457         // remove Card from items.
2458         
2459         var old_parent = data.source.parent();
2460         
2461         old_parent.items = old_parent.items.filter(function(e) { return e != data.source });
2462         
2463         if (this.items.length) {
2464             var nitems = [];
2465             //Roo.log([info.items_n, info.position, this.items.length]);
2466             for (var i =0; i < this.items.length; i++) {
2467                 if (i == info.items_n && info.position == 'above') {
2468                     nitems.push(data.source);
2469                 }
2470                 nitems.push(this.items[i]);
2471                 if (i == info.items_n && info.position == 'below') {
2472                     nitems.push(data.source);
2473                 }
2474             }
2475             this.items = nitems;
2476             Roo.log(this.items);
2477         } else {
2478             this.items.push(data.source);
2479         }
2480         
2481         data.source.parentId = this.id;
2482         
2483         return true;
2484     },
2485     
2486     /**    Decide whether to drop above or below a View node. */
2487     getDropPoint : function(e, n, dd)
2488     {
2489         if (dd) {
2490              return false;
2491         }
2492         if (n == this.bodyEl.dom) {
2493             return "above";
2494         }
2495         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2496         var c = t + (b - t) / 2;
2497         var y = Roo.lib.Event.getPageY(e);
2498         if(y <= c) {
2499             return "above";
2500         }else{
2501             return "below";
2502         }
2503     },
2504     onToggleCollapse : function(e)
2505         {
2506         if (this.collapsed) {
2507             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2508             this.collapsableEl.addClass('show');
2509             this.collapsed = false;
2510             return;
2511         }
2512         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2513         this.collapsableEl.removeClass('show');
2514         this.collapsed = true;
2515         
2516     
2517     },
2518     
2519     onToggleRotate : function(e)
2520     {
2521         this.collapsableEl.removeClass('show');
2522         this.footerEl.removeClass('d-none');
2523         this.el.removeClass('roo-card-rotated');
2524         this.el.removeClass('d-none');
2525         if (this.rotated) {
2526             
2527             this.collapsableEl.addClass('show');
2528             this.rotated = false;
2529             this.fireEvent('rotate', this, this.rotated);
2530             return;
2531         }
2532         this.el.addClass('roo-card-rotated');
2533         this.footerEl.addClass('d-none');
2534         this.el.select('.roo-collapsable').removeClass('show');
2535         
2536         this.rotated = true;
2537         this.fireEvent('rotate', this, this.rotated);
2538     
2539     },
2540     
2541     dropPlaceHolder: function (action, info, data)
2542     {
2543         if (this.dropEl === false) {
2544             this.dropEl = Roo.DomHelper.append(this.bodyEl, {
2545             cls : 'd-none'
2546             },true);
2547         }
2548         this.dropEl.removeClass(['d-none', 'd-block']);        
2549         if (action == 'hide') {
2550             
2551             this.dropEl.addClass('d-none');
2552             return;
2553         }
2554         // FIXME - info.card == true!!!
2555         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2556         
2557         if (info.card !== true) {
2558             var cardel = info.card.el.dom;
2559             
2560             if (info.position == 'above') {
2561                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2562             } else if (cardel.nextSibling) {
2563                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2564             } else {
2565                 cardel.parentNode.append(this.dropEl.dom);
2566             }
2567         } else {
2568             // card container???
2569             this.bodyEl.dom.append(this.dropEl.dom);
2570         }
2571         
2572         this.dropEl.addClass('d-block roo-card-dropzone');
2573         
2574         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2575         
2576         
2577     
2578     
2579     
2580     },
2581     setHeaderText: function(html)
2582     {
2583         this.headerEl.dom.innerHTML = html;
2584     }
2585
2586     
2587 });
2588
2589 /*
2590  * - LGPL
2591  *
2592  * Card header - holder for the card header elements.
2593  * 
2594  */
2595
2596 /**
2597  * @class Roo.bootstrap.CardHeader
2598  * @extends Roo.bootstrap.Element
2599  * Bootstrap CardHeader class
2600  * @constructor
2601  * Create a new Card Header - that you can embed children into
2602  * @param {Object} config The config object
2603  */
2604
2605 Roo.bootstrap.CardHeader = function(config){
2606     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2607 };
2608
2609 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2610     
2611     
2612     container_method : 'getCardHeader' 
2613     
2614      
2615     
2616     
2617    
2618 });
2619
2620  
2621
2622  /*
2623  * - LGPL
2624  *
2625  * Card footer - holder for the card footer elements.
2626  * 
2627  */
2628
2629 /**
2630  * @class Roo.bootstrap.CardFooter
2631  * @extends Roo.bootstrap.Element
2632  * Bootstrap CardFooter class
2633  * @constructor
2634  * Create a new Card Footer - that you can embed children into
2635  * @param {Object} config The config object
2636  */
2637
2638 Roo.bootstrap.CardFooter = function(config){
2639     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2640 };
2641
2642 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2643     
2644     
2645     container_method : 'getCardFooter' 
2646     
2647      
2648     
2649     
2650    
2651 });
2652
2653  
2654
2655  /*
2656  * - LGPL
2657  *
2658  * Card header - holder for the card header elements.
2659  * 
2660  */
2661
2662 /**
2663  * @class Roo.bootstrap.CardImageTop
2664  * @extends Roo.bootstrap.Element
2665  * Bootstrap CardImageTop class
2666  * @constructor
2667  * Create a new Card Image Top container
2668  * @param {Object} config The config object
2669  */
2670
2671 Roo.bootstrap.CardImageTop = function(config){
2672     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2673 };
2674
2675 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2676     
2677    
2678     container_method : 'getCardImageTop' 
2679     
2680      
2681     
2682    
2683 });
2684
2685  
2686
2687  /*
2688  * - LGPL
2689  *
2690  * image
2691  * 
2692  */
2693
2694
2695 /**
2696  * @class Roo.bootstrap.Img
2697  * @extends Roo.bootstrap.Component
2698  * Bootstrap Img class
2699  * @cfg {Boolean} imgResponsive false | true
2700  * @cfg {String} border rounded | circle | thumbnail
2701  * @cfg {String} src image source
2702  * @cfg {String} alt image alternative text
2703  * @cfg {String} href a tag href
2704  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2705  * @cfg {String} xsUrl xs image source
2706  * @cfg {String} smUrl sm image source
2707  * @cfg {String} mdUrl md image source
2708  * @cfg {String} lgUrl lg image source
2709  * 
2710  * @constructor
2711  * Create a new Input
2712  * @param {Object} config The config object
2713  */
2714
2715 Roo.bootstrap.Img = function(config){
2716     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2717     
2718     this.addEvents({
2719         // img events
2720         /**
2721          * @event click
2722          * The img click event for the img.
2723          * @param {Roo.EventObject} e
2724          */
2725         "click" : true
2726     });
2727 };
2728
2729 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2730     
2731     imgResponsive: true,
2732     border: '',
2733     src: 'about:blank',
2734     href: false,
2735     target: false,
2736     xsUrl: '',
2737     smUrl: '',
2738     mdUrl: '',
2739     lgUrl: '',
2740
2741     getAutoCreate : function()
2742     {   
2743         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2744             return this.createSingleImg();
2745         }
2746         
2747         var cfg = {
2748             tag: 'div',
2749             cls: 'roo-image-responsive-group',
2750             cn: []
2751         };
2752         var _this = this;
2753         
2754         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2755             
2756             if(!_this[size + 'Url']){
2757                 return;
2758             }
2759             
2760             var img = {
2761                 tag: 'img',
2762                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2763                 html: _this.html || cfg.html,
2764                 src: _this[size + 'Url']
2765             };
2766             
2767             img.cls += ' roo-image-responsive-' + size;
2768             
2769             var s = ['xs', 'sm', 'md', 'lg'];
2770             
2771             s.splice(s.indexOf(size), 1);
2772             
2773             Roo.each(s, function(ss){
2774                 img.cls += ' hidden-' + ss;
2775             });
2776             
2777             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2778                 cfg.cls += ' img-' + _this.border;
2779             }
2780             
2781             if(_this.alt){
2782                 cfg.alt = _this.alt;
2783             }
2784             
2785             if(_this.href){
2786                 var a = {
2787                     tag: 'a',
2788                     href: _this.href,
2789                     cn: [
2790                         img
2791                     ]
2792                 };
2793
2794                 if(this.target){
2795                     a.target = _this.target;
2796                 }
2797             }
2798             
2799             cfg.cn.push((_this.href) ? a : img);
2800             
2801         });
2802         
2803         return cfg;
2804     },
2805     
2806     createSingleImg : function()
2807     {
2808         var cfg = {
2809             tag: 'img',
2810             cls: (this.imgResponsive) ? 'img-responsive' : '',
2811             html : null,
2812             src : 'about:blank'  // just incase src get's set to undefined?!?
2813         };
2814         
2815         cfg.html = this.html || cfg.html;
2816         
2817         cfg.src = this.src || cfg.src;
2818         
2819         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2820             cfg.cls += ' img-' + this.border;
2821         }
2822         
2823         if(this.alt){
2824             cfg.alt = this.alt;
2825         }
2826         
2827         if(this.href){
2828             var a = {
2829                 tag: 'a',
2830                 href: this.href,
2831                 cn: [
2832                     cfg
2833                 ]
2834             };
2835             
2836             if(this.target){
2837                 a.target = this.target;
2838             }
2839             
2840         }
2841         
2842         return (this.href) ? a : cfg;
2843     },
2844     
2845     initEvents: function() 
2846     {
2847         if(!this.href){
2848             this.el.on('click', this.onClick, this);
2849         }
2850         
2851     },
2852     
2853     onClick : function(e)
2854     {
2855         Roo.log('img onclick');
2856         this.fireEvent('click', this, e);
2857     },
2858     /**
2859      * Sets the url of the image - used to update it
2860      * @param {String} url the url of the image
2861      */
2862     
2863     setSrc : function(url)
2864     {
2865         this.src =  url;
2866         
2867         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2868             this.el.dom.src =  url;
2869             return;
2870         }
2871         
2872         this.el.select('img', true).first().dom.src =  url;
2873     }
2874     
2875     
2876    
2877 });
2878
2879  /*
2880  * - LGPL
2881  *
2882  * image
2883  * 
2884  */
2885
2886
2887 /**
2888  * @class Roo.bootstrap.Link
2889  * @extends Roo.bootstrap.Component
2890  * Bootstrap Link Class
2891  * @cfg {String} alt image alternative text
2892  * @cfg {String} href a tag href
2893  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2894  * @cfg {String} html the content of the link.
2895  * @cfg {String} anchor name for the anchor link
2896  * @cfg {String} fa - favicon
2897
2898  * @cfg {Boolean} preventDefault (true | false) default false
2899
2900  * 
2901  * @constructor
2902  * Create a new Input
2903  * @param {Object} config The config object
2904  */
2905
2906 Roo.bootstrap.Link = function(config){
2907     Roo.bootstrap.Link.superclass.constructor.call(this, config);
2908     
2909     this.addEvents({
2910         // img events
2911         /**
2912          * @event click
2913          * The img click event for the img.
2914          * @param {Roo.EventObject} e
2915          */
2916         "click" : true
2917     });
2918 };
2919
2920 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
2921     
2922     href: false,
2923     target: false,
2924     preventDefault: false,
2925     anchor : false,
2926     alt : false,
2927     fa: false,
2928
2929
2930     getAutoCreate : function()
2931     {
2932         var html = this.html || '';
2933         
2934         if (this.fa !== false) {
2935             html = '<i class="fa fa-' + this.fa + '"></i>';
2936         }
2937         var cfg = {
2938             tag: 'a'
2939         };
2940         // anchor's do not require html/href...
2941         if (this.anchor === false) {
2942             cfg.html = html;
2943             cfg.href = this.href || '#';
2944         } else {
2945             cfg.name = this.anchor;
2946             if (this.html !== false || this.fa !== false) {
2947                 cfg.html = html;
2948             }
2949             if (this.href !== false) {
2950                 cfg.href = this.href;
2951             }
2952         }
2953         
2954         if(this.alt !== false){
2955             cfg.alt = this.alt;
2956         }
2957         
2958         
2959         if(this.target !== false) {
2960             cfg.target = this.target;
2961         }
2962         
2963         return cfg;
2964     },
2965     
2966     initEvents: function() {
2967         
2968         if(!this.href || this.preventDefault){
2969             this.el.on('click', this.onClick, this);
2970         }
2971     },
2972     
2973     onClick : function(e)
2974     {
2975         if(this.preventDefault){
2976             e.preventDefault();
2977         }
2978         //Roo.log('img onclick');
2979         this.fireEvent('click', this, e);
2980     }
2981    
2982 });
2983
2984  /*
2985  * - LGPL
2986  *
2987  * header
2988  * 
2989  */
2990
2991 /**
2992  * @class Roo.bootstrap.Header
2993  * @extends Roo.bootstrap.Component
2994  * Bootstrap Header class
2995  * @cfg {String} html content of header
2996  * @cfg {Number} level (1|2|3|4|5|6) default 1
2997  * 
2998  * @constructor
2999  * Create a new Header
3000  * @param {Object} config The config object
3001  */
3002
3003
3004 Roo.bootstrap.Header  = function(config){
3005     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3006 };
3007
3008 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3009     
3010     //href : false,
3011     html : false,
3012     level : 1,
3013     
3014     
3015     
3016     getAutoCreate : function(){
3017         
3018         
3019         
3020         var cfg = {
3021             tag: 'h' + (1 *this.level),
3022             html: this.html || ''
3023         } ;
3024         
3025         return cfg;
3026     }
3027    
3028 });
3029
3030  
3031
3032  /*
3033  * Based on:
3034  * Ext JS Library 1.1.1
3035  * Copyright(c) 2006-2007, Ext JS, LLC.
3036  *
3037  * Originally Released Under LGPL - original licence link has changed is not relivant.
3038  *
3039  * Fork - LGPL
3040  * <script type="text/javascript">
3041  */
3042  
3043 /**
3044  * @class Roo.bootstrap.MenuMgr
3045  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3046  * @singleton
3047  */
3048 Roo.bootstrap.MenuMgr = function(){
3049    var menus, active, groups = {}, attached = false, lastShow = new Date();
3050
3051    // private - called when first menu is created
3052    function init(){
3053        menus = {};
3054        active = new Roo.util.MixedCollection();
3055        Roo.get(document).addKeyListener(27, function(){
3056            if(active.length > 0){
3057                hideAll();
3058            }
3059        });
3060    }
3061
3062    // private
3063    function hideAll(){
3064        if(active && active.length > 0){
3065            var c = active.clone();
3066            c.each(function(m){
3067                m.hide();
3068            });
3069        }
3070    }
3071
3072    // private
3073    function onHide(m){
3074        active.remove(m);
3075        if(active.length < 1){
3076            Roo.get(document).un("mouseup", onMouseDown);
3077             
3078            attached = false;
3079        }
3080    }
3081
3082    // private
3083    function onShow(m){
3084        var last = active.last();
3085        lastShow = new Date();
3086        active.add(m);
3087        if(!attached){
3088           Roo.get(document).on("mouseup", onMouseDown);
3089            
3090            attached = true;
3091        }
3092        if(m.parentMenu){
3093           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3094           m.parentMenu.activeChild = m;
3095        }else if(last && last.isVisible()){
3096           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3097        }
3098    }
3099
3100    // private
3101    function onBeforeHide(m){
3102        if(m.activeChild){
3103            m.activeChild.hide();
3104        }
3105        if(m.autoHideTimer){
3106            clearTimeout(m.autoHideTimer);
3107            delete m.autoHideTimer;
3108        }
3109    }
3110
3111    // private
3112    function onBeforeShow(m){
3113        var pm = m.parentMenu;
3114        if(!pm && !m.allowOtherMenus){
3115            hideAll();
3116        }else if(pm && pm.activeChild && active != m){
3117            pm.activeChild.hide();
3118        }
3119    }
3120
3121    // private this should really trigger on mouseup..
3122    function onMouseDown(e){
3123         Roo.log("on Mouse Up");
3124         
3125         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3126             Roo.log("MenuManager hideAll");
3127             hideAll();
3128             e.stopEvent();
3129         }
3130         
3131         
3132    }
3133
3134    // private
3135    function onBeforeCheck(mi, state){
3136        if(state){
3137            var g = groups[mi.group];
3138            for(var i = 0, l = g.length; i < l; i++){
3139                if(g[i] != mi){
3140                    g[i].setChecked(false);
3141                }
3142            }
3143        }
3144    }
3145
3146    return {
3147
3148        /**
3149         * Hides all menus that are currently visible
3150         */
3151        hideAll : function(){
3152             hideAll();  
3153        },
3154
3155        // private
3156        register : function(menu){
3157            if(!menus){
3158                init();
3159            }
3160            menus[menu.id] = menu;
3161            menu.on("beforehide", onBeforeHide);
3162            menu.on("hide", onHide);
3163            menu.on("beforeshow", onBeforeShow);
3164            menu.on("show", onShow);
3165            var g = menu.group;
3166            if(g && menu.events["checkchange"]){
3167                if(!groups[g]){
3168                    groups[g] = [];
3169                }
3170                groups[g].push(menu);
3171                menu.on("checkchange", onCheck);
3172            }
3173        },
3174
3175         /**
3176          * Returns a {@link Roo.menu.Menu} object
3177          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3178          * be used to generate and return a new Menu instance.
3179          */
3180        get : function(menu){
3181            if(typeof menu == "string"){ // menu id
3182                return menus[menu];
3183            }else if(menu.events){  // menu instance
3184                return menu;
3185            }
3186            /*else if(typeof menu.length == 'number'){ // array of menu items?
3187                return new Roo.bootstrap.Menu({items:menu});
3188            }else{ // otherwise, must be a config
3189                return new Roo.bootstrap.Menu(menu);
3190            }
3191            */
3192            return false;
3193        },
3194
3195        // private
3196        unregister : function(menu){
3197            delete menus[menu.id];
3198            menu.un("beforehide", onBeforeHide);
3199            menu.un("hide", onHide);
3200            menu.un("beforeshow", onBeforeShow);
3201            menu.un("show", onShow);
3202            var g = menu.group;
3203            if(g && menu.events["checkchange"]){
3204                groups[g].remove(menu);
3205                menu.un("checkchange", onCheck);
3206            }
3207        },
3208
3209        // private
3210        registerCheckable : function(menuItem){
3211            var g = menuItem.group;
3212            if(g){
3213                if(!groups[g]){
3214                    groups[g] = [];
3215                }
3216                groups[g].push(menuItem);
3217                menuItem.on("beforecheckchange", onBeforeCheck);
3218            }
3219        },
3220
3221        // private
3222        unregisterCheckable : function(menuItem){
3223            var g = menuItem.group;
3224            if(g){
3225                groups[g].remove(menuItem);
3226                menuItem.un("beforecheckchange", onBeforeCheck);
3227            }
3228        }
3229    };
3230 }();/*
3231  * - LGPL
3232  *
3233  * menu
3234  * 
3235  */
3236
3237 /**
3238  * @class Roo.bootstrap.Menu
3239  * @extends Roo.bootstrap.Component
3240  * Bootstrap Menu class - container for MenuItems
3241  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3242  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3243  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3244  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3245  * 
3246  * @constructor
3247  * Create a new Menu
3248  * @param {Object} config The config object
3249  */
3250
3251
3252 Roo.bootstrap.Menu = function(config){
3253     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3254     if (this.registerMenu && this.type != 'treeview')  {
3255         Roo.bootstrap.MenuMgr.register(this);
3256     }
3257     
3258     
3259     this.addEvents({
3260         /**
3261          * @event beforeshow
3262          * Fires before this menu is displayed (return false to block)
3263          * @param {Roo.menu.Menu} this
3264          */
3265         beforeshow : true,
3266         /**
3267          * @event beforehide
3268          * Fires before this menu is hidden (return false to block)
3269          * @param {Roo.menu.Menu} this
3270          */
3271         beforehide : true,
3272         /**
3273          * @event show
3274          * Fires after this menu is displayed
3275          * @param {Roo.menu.Menu} this
3276          */
3277         show : true,
3278         /**
3279          * @event hide
3280          * Fires after this menu is hidden
3281          * @param {Roo.menu.Menu} this
3282          */
3283         hide : true,
3284         /**
3285          * @event click
3286          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3287          * @param {Roo.menu.Menu} this
3288          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3289          * @param {Roo.EventObject} e
3290          */
3291         click : true,
3292         /**
3293          * @event mouseover
3294          * Fires when the mouse is hovering over this menu
3295          * @param {Roo.menu.Menu} this
3296          * @param {Roo.EventObject} e
3297          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3298          */
3299         mouseover : true,
3300         /**
3301          * @event mouseout
3302          * Fires when the mouse exits this menu
3303          * @param {Roo.menu.Menu} this
3304          * @param {Roo.EventObject} e
3305          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3306          */
3307         mouseout : true,
3308         /**
3309          * @event itemclick
3310          * Fires when a menu item contained in this menu is clicked
3311          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3312          * @param {Roo.EventObject} e
3313          */
3314         itemclick: true
3315     });
3316     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3317 };
3318
3319 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3320     
3321    /// html : false,
3322     //align : '',
3323     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3324     type: false,
3325     /**
3326      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3327      */
3328     registerMenu : true,
3329     
3330     menuItems :false, // stores the menu items..
3331     
3332     hidden:true,
3333         
3334     parentMenu : false,
3335     
3336     stopEvent : true,
3337     
3338     isLink : false,
3339     
3340     getChildContainer : function() {
3341         return this.el;  
3342     },
3343     
3344     getAutoCreate : function(){
3345          
3346         //if (['right'].indexOf(this.align)!==-1) {
3347         //    cfg.cn[1].cls += ' pull-right'
3348         //}
3349         
3350         
3351         var cfg = {
3352             tag : 'ul',
3353             cls : 'dropdown-menu' ,
3354             style : 'z-index:1000'
3355             
3356         };
3357         
3358         if (this.type === 'submenu') {
3359             cfg.cls = 'submenu active';
3360         }
3361         if (this.type === 'treeview') {
3362             cfg.cls = 'treeview-menu';
3363         }
3364         
3365         return cfg;
3366     },
3367     initEvents : function() {
3368         
3369        // Roo.log("ADD event");
3370        // Roo.log(this.triggerEl.dom);
3371         
3372         this.triggerEl.on('click', this.onTriggerClick, this);
3373         
3374         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3375         
3376         
3377         if (this.triggerEl.hasClass('nav-item')) {
3378             // dropdown toggle on the 'a' in BS4?
3379             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3380         } else {
3381             this.triggerEl.addClass('dropdown-toggle');
3382         }
3383         if (Roo.isTouch) {
3384             this.el.on('touchstart'  , this.onTouch, this);
3385         }
3386         this.el.on('click' , this.onClick, this);
3387
3388         this.el.on("mouseover", this.onMouseOver, this);
3389         this.el.on("mouseout", this.onMouseOut, this);
3390         
3391     },
3392     
3393     findTargetItem : function(e)
3394     {
3395         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3396         if(!t){
3397             return false;
3398         }
3399         //Roo.log(t);         Roo.log(t.id);
3400         if(t && t.id){
3401             //Roo.log(this.menuitems);
3402             return this.menuitems.get(t.id);
3403             
3404             //return this.items.get(t.menuItemId);
3405         }
3406         
3407         return false;
3408     },
3409     
3410     onTouch : function(e) 
3411     {
3412         Roo.log("menu.onTouch");
3413         //e.stopEvent(); this make the user popdown broken
3414         this.onClick(e);
3415     },
3416     
3417     onClick : function(e)
3418     {
3419         Roo.log("menu.onClick");
3420         
3421         var t = this.findTargetItem(e);
3422         if(!t || t.isContainer){
3423             return;
3424         }
3425         Roo.log(e);
3426         /*
3427         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3428             if(t == this.activeItem && t.shouldDeactivate(e)){
3429                 this.activeItem.deactivate();
3430                 delete this.activeItem;
3431                 return;
3432             }
3433             if(t.canActivate){
3434                 this.setActiveItem(t, true);
3435             }
3436             return;
3437             
3438             
3439         }
3440         */
3441        
3442         Roo.log('pass click event');
3443         
3444         t.onClick(e);
3445         
3446         this.fireEvent("click", this, t, e);
3447         
3448         var _this = this;
3449         
3450         if(!t.href.length || t.href == '#'){
3451             (function() { _this.hide(); }).defer(100);
3452         }
3453         
3454     },
3455     
3456     onMouseOver : function(e){
3457         var t  = this.findTargetItem(e);
3458         //Roo.log(t);
3459         //if(t){
3460         //    if(t.canActivate && !t.disabled){
3461         //        this.setActiveItem(t, true);
3462         //    }
3463         //}
3464         
3465         this.fireEvent("mouseover", this, e, t);
3466     },
3467     isVisible : function(){
3468         return !this.hidden;
3469     },
3470     onMouseOut : function(e){
3471         var t  = this.findTargetItem(e);
3472         
3473         //if(t ){
3474         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3475         //        this.activeItem.deactivate();
3476         //        delete this.activeItem;
3477         //    }
3478         //}
3479         this.fireEvent("mouseout", this, e, t);
3480     },
3481     
3482     
3483     /**
3484      * Displays this menu relative to another element
3485      * @param {String/HTMLElement/Roo.Element} element The element to align to
3486      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3487      * the element (defaults to this.defaultAlign)
3488      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3489      */
3490     show : function(el, pos, parentMenu)
3491     {
3492         if (false === this.fireEvent("beforeshow", this)) {
3493             Roo.log("show canceled");
3494             return;
3495         }
3496         this.parentMenu = parentMenu;
3497         if(!this.el){
3498             this.render();
3499         }
3500         
3501         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3502     },
3503      /**
3504      * Displays this menu at a specific xy position
3505      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3506      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3507      */
3508     showAt : function(xy, parentMenu, /* private: */_e){
3509         this.parentMenu = parentMenu;
3510         if(!this.el){
3511             this.render();
3512         }
3513         if(_e !== false){
3514             this.fireEvent("beforeshow", this);
3515             //xy = this.el.adjustForConstraints(xy);
3516         }
3517         
3518         //this.el.show();
3519         this.hideMenuItems();
3520         this.hidden = false;
3521         this.triggerEl.addClass('open');
3522         this.el.addClass('show');
3523         
3524         // reassign x when hitting right
3525         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3526             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3527         }
3528         
3529         // reassign y when hitting bottom
3530         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3531             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3532         }
3533         
3534         // but the list may align on trigger left or trigger top... should it be a properity?
3535         
3536         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3537             this.el.setXY(xy);
3538         }
3539         
3540         this.focus();
3541         this.fireEvent("show", this);
3542     },
3543     
3544     focus : function(){
3545         return;
3546         if(!this.hidden){
3547             this.doFocus.defer(50, this);
3548         }
3549     },
3550
3551     doFocus : function(){
3552         if(!this.hidden){
3553             this.focusEl.focus();
3554         }
3555     },
3556
3557     /**
3558      * Hides this menu and optionally all parent menus
3559      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3560      */
3561     hide : function(deep)
3562     {
3563         if (false === this.fireEvent("beforehide", this)) {
3564             Roo.log("hide canceled");
3565             return;
3566         }
3567         this.hideMenuItems();
3568         if(this.el && this.isVisible()){
3569            
3570             if(this.activeItem){
3571                 this.activeItem.deactivate();
3572                 this.activeItem = null;
3573             }
3574             this.triggerEl.removeClass('open');;
3575             this.el.removeClass('show');
3576             this.hidden = true;
3577             this.fireEvent("hide", this);
3578         }
3579         if(deep === true && this.parentMenu){
3580             this.parentMenu.hide(true);
3581         }
3582     },
3583     
3584     onTriggerClick : function(e)
3585     {
3586         Roo.log('trigger click');
3587         
3588         var target = e.getTarget();
3589         
3590         Roo.log(target.nodeName.toLowerCase());
3591         
3592         if(target.nodeName.toLowerCase() === 'i'){
3593             e.preventDefault();
3594         }
3595         
3596     },
3597     
3598     onTriggerPress  : function(e)
3599     {
3600         Roo.log('trigger press');
3601         //Roo.log(e.getTarget());
3602        // Roo.log(this.triggerEl.dom);
3603        
3604         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3605         var pel = Roo.get(e.getTarget());
3606         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3607             Roo.log('is treeview or dropdown?');
3608             return;
3609         }
3610         
3611         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3612             return;
3613         }
3614         
3615         if (this.isVisible()) {
3616             Roo.log('hide');
3617             this.hide();
3618         } else {
3619             Roo.log('show');
3620             this.show(this.triggerEl, '?', false);
3621         }
3622         
3623         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3624             e.stopEvent();
3625         }
3626         
3627     },
3628        
3629     
3630     hideMenuItems : function()
3631     {
3632         Roo.log("hide Menu Items");
3633         if (!this.el) { 
3634             return;
3635         }
3636         
3637         this.el.select('.open',true).each(function(aa) {
3638             
3639             aa.removeClass('open');
3640          
3641         });
3642     },
3643     addxtypeChild : function (tree, cntr) {
3644         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3645           
3646         this.menuitems.add(comp);
3647         return comp;
3648
3649     },
3650     getEl : function()
3651     {
3652         Roo.log(this.el);
3653         return this.el;
3654     },
3655     
3656     clear : function()
3657     {
3658         this.getEl().dom.innerHTML = '';
3659         this.menuitems.clear();
3660     }
3661 });
3662
3663  
3664  /*
3665  * - LGPL
3666  *
3667  * menu item
3668  * 
3669  */
3670
3671
3672 /**
3673  * @class Roo.bootstrap.MenuItem
3674  * @extends Roo.bootstrap.Component
3675  * Bootstrap MenuItem class
3676  * @cfg {String} html the menu label
3677  * @cfg {String} href the link
3678  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3679  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3680  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3681  * @cfg {String} fa favicon to show on left of menu item.
3682  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3683  * 
3684  * 
3685  * @constructor
3686  * Create a new MenuItem
3687  * @param {Object} config The config object
3688  */
3689
3690
3691 Roo.bootstrap.MenuItem = function(config){
3692     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3693     this.addEvents({
3694         // raw events
3695         /**
3696          * @event click
3697          * The raw click event for the entire grid.
3698          * @param {Roo.bootstrap.MenuItem} this
3699          * @param {Roo.EventObject} e
3700          */
3701         "click" : true
3702     });
3703 };
3704
3705 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3706     
3707     href : false,
3708     html : false,
3709     preventDefault: false,
3710     isContainer : false,
3711     active : false,
3712     fa: false,
3713     
3714     getAutoCreate : function(){
3715         
3716         if(this.isContainer){
3717             return {
3718                 tag: 'li',
3719                 cls: 'dropdown-menu-item '
3720             };
3721         }
3722         var ctag = {
3723             tag: 'span',
3724             html: 'Link'
3725         };
3726         
3727         var anc = {
3728             tag : 'a',
3729             cls : 'dropdown-item',
3730             href : '#',
3731             cn : [  ]
3732         };
3733         
3734         if (this.fa !== false) {
3735             anc.cn.push({
3736                 tag : 'i',
3737                 cls : 'fa fa-' + this.fa
3738             });
3739         }
3740         
3741         anc.cn.push(ctag);
3742         
3743         
3744         var cfg= {
3745             tag: 'li',
3746             cls: 'dropdown-menu-item',
3747             cn: [ anc ]
3748         };
3749         if (this.parent().type == 'treeview') {
3750             cfg.cls = 'treeview-menu';
3751         }
3752         if (this.active) {
3753             cfg.cls += ' active';
3754         }
3755         
3756         
3757         
3758         anc.href = this.href || cfg.cn[0].href ;
3759         ctag.html = this.html || cfg.cn[0].html ;
3760         return cfg;
3761     },
3762     
3763     initEvents: function()
3764     {
3765         if (this.parent().type == 'treeview') {
3766             this.el.select('a').on('click', this.onClick, this);
3767         }
3768         
3769         if (this.menu) {
3770             this.menu.parentType = this.xtype;
3771             this.menu.triggerEl = this.el;
3772             this.menu = this.addxtype(Roo.apply({}, this.menu));
3773         }
3774         
3775     },
3776     onClick : function(e)
3777     {
3778         Roo.log('item on click ');
3779         
3780         if(this.preventDefault){
3781             e.preventDefault();
3782         }
3783         //this.parent().hideMenuItems();
3784         
3785         this.fireEvent('click', this, e);
3786     },
3787     getEl : function()
3788     {
3789         return this.el;
3790     } 
3791 });
3792
3793  
3794
3795  /*
3796  * - LGPL
3797  *
3798  * menu separator
3799  * 
3800  */
3801
3802
3803 /**
3804  * @class Roo.bootstrap.MenuSeparator
3805  * @extends Roo.bootstrap.Component
3806  * Bootstrap MenuSeparator class
3807  * 
3808  * @constructor
3809  * Create a new MenuItem
3810  * @param {Object} config The config object
3811  */
3812
3813
3814 Roo.bootstrap.MenuSeparator = function(config){
3815     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3816 };
3817
3818 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3819     
3820     getAutoCreate : function(){
3821         var cfg = {
3822             cls: 'divider',
3823             tag : 'li'
3824         };
3825         
3826         return cfg;
3827     }
3828    
3829 });
3830
3831  
3832
3833  
3834 /*
3835 * Licence: LGPL
3836 */
3837
3838 /**
3839  * @class Roo.bootstrap.Modal
3840  * @extends Roo.bootstrap.Component
3841  * Bootstrap Modal class
3842  * @cfg {String} title Title of dialog
3843  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3844  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3845  * @cfg {Boolean} specificTitle default false
3846  * @cfg {Array} buttons Array of buttons or standard button set..
3847  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3848  * @cfg {Boolean} animate default true
3849  * @cfg {Boolean} allow_close default true
3850  * @cfg {Boolean} fitwindow default false
3851  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3852  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3853  * @cfg {String} size (sm|lg|xl) default empty
3854  * @cfg {Number} max_width set the max width of modal
3855  * @cfg {Boolean} editableTitle can the title be edited
3856
3857  *
3858  *
3859  * @constructor
3860  * Create a new Modal Dialog
3861  * @param {Object} config The config object
3862  */
3863
3864 Roo.bootstrap.Modal = function(config){
3865     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3866     this.addEvents({
3867         // raw events
3868         /**
3869          * @event btnclick
3870          * The raw btnclick event for the button
3871          * @param {Roo.EventObject} e
3872          */
3873         "btnclick" : true,
3874         /**
3875          * @event resize
3876          * Fire when dialog resize
3877          * @param {Roo.bootstrap.Modal} this
3878          * @param {Roo.EventObject} e
3879          */
3880         "resize" : true,
3881         /**
3882          * @event titlechanged
3883          * Fire when the editable title has been changed
3884          * @param {Roo.bootstrap.Modal} this
3885          * @param {Roo.EventObject} value
3886          */
3887         "titlechanged" : true 
3888         
3889     });
3890     this.buttons = this.buttons || [];
3891
3892     if (this.tmpl) {
3893         this.tmpl = Roo.factory(this.tmpl);
3894     }
3895
3896 };
3897
3898 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3899
3900     title : 'test dialog',
3901
3902     buttons : false,
3903
3904     // set on load...
3905
3906     html: false,
3907
3908     tmp: false,
3909
3910     specificTitle: false,
3911
3912     buttonPosition: 'right',
3913
3914     allow_close : true,
3915
3916     animate : true,
3917
3918     fitwindow: false,
3919     
3920      // private
3921     dialogEl: false,
3922     bodyEl:  false,
3923     footerEl:  false,
3924     titleEl:  false,
3925     closeEl:  false,
3926
3927     size: '',
3928     
3929     max_width: 0,
3930     
3931     max_height: 0,
3932     
3933     fit_content: false,
3934     editableTitle  : false,
3935
3936     onRender : function(ct, position)
3937     {
3938         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
3939
3940         if(!this.el){
3941             var cfg = Roo.apply({},  this.getAutoCreate());
3942             cfg.id = Roo.id();
3943             //if(!cfg.name){
3944             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
3945             //}
3946             //if (!cfg.name.length) {
3947             //    delete cfg.name;
3948            // }
3949             if (this.cls) {
3950                 cfg.cls += ' ' + this.cls;
3951             }
3952             if (this.style) {
3953                 cfg.style = this.style;
3954             }
3955             this.el = Roo.get(document.body).createChild(cfg, position);
3956         }
3957         //var type = this.el.dom.type;
3958
3959
3960         if(this.tabIndex !== undefined){
3961             this.el.dom.setAttribute('tabIndex', this.tabIndex);
3962         }
3963
3964         this.dialogEl = this.el.select('.modal-dialog',true).first();
3965         this.bodyEl = this.el.select('.modal-body',true).first();
3966         this.closeEl = this.el.select('.modal-header .close', true).first();
3967         this.headerEl = this.el.select('.modal-header',true).first();
3968         this.titleEl = this.el.select('.modal-title',true).first();
3969         this.footerEl = this.el.select('.modal-footer',true).first();
3970
3971         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
3972         
3973         //this.el.addClass("x-dlg-modal");
3974
3975         if (this.buttons.length) {
3976             Roo.each(this.buttons, function(bb) {
3977                 var b = Roo.apply({}, bb);
3978                 b.xns = b.xns || Roo.bootstrap;
3979                 b.xtype = b.xtype || 'Button';
3980                 if (typeof(b.listeners) == 'undefined') {
3981                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
3982                 }
3983
3984                 var btn = Roo.factory(b);
3985
3986                 btn.render(this.getButtonContainer());
3987
3988             },this);
3989         }
3990         // render the children.
3991         var nitems = [];
3992
3993         if(typeof(this.items) != 'undefined'){
3994             var items = this.items;
3995             delete this.items;
3996
3997             for(var i =0;i < items.length;i++) {
3998                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
3999             }
4000         }
4001
4002         this.items = nitems;
4003
4004         // where are these used - they used to be body/close/footer
4005
4006
4007         this.initEvents();
4008         //this.el.addClass([this.fieldClass, this.cls]);
4009
4010     },
4011
4012     getAutoCreate : function()
4013     {
4014         // we will default to modal-body-overflow - might need to remove or make optional later.
4015         var bdy = {
4016                 cls : 'modal-body enable-modal-body-overflow ', 
4017                 html : this.html || ''
4018         };
4019
4020         var title = {
4021             tag: 'h4',
4022             cls : 'modal-title',
4023             html : this.title
4024         };
4025
4026         if(this.specificTitle){ // WTF is this?
4027             title = this.title;
4028         }
4029
4030         var header = [];
4031         if (this.allow_close && Roo.bootstrap.version == 3) {
4032             header.push({
4033                 tag: 'button',
4034                 cls : 'close',
4035                 html : '&times'
4036             });
4037         }
4038
4039         header.push(title);
4040
4041         if (this.editableTitle) {
4042             header.push({
4043                 cls: 'form-control roo-editable-title d-none',
4044                 tag: 'input',
4045                 type: 'text'
4046             });
4047         }
4048         
4049         if (this.allow_close && Roo.bootstrap.version == 4) {
4050             header.push({
4051                 tag: 'button',
4052                 cls : 'close',
4053                 html : '&times'
4054             });
4055         }
4056         
4057         var size = '';
4058
4059         if(this.size.length){
4060             size = 'modal-' + this.size;
4061         }
4062         
4063         var footer = Roo.bootstrap.version == 3 ?
4064             {
4065                 cls : 'modal-footer',
4066                 cn : [
4067                     {
4068                         tag: 'div',
4069                         cls: 'btn-' + this.buttonPosition
4070                     }
4071                 ]
4072
4073             } :
4074             {  // BS4 uses mr-auto on left buttons....
4075                 cls : 'modal-footer'
4076             };
4077
4078             
4079
4080         
4081         
4082         var modal = {
4083             cls: "modal",
4084              cn : [
4085                 {
4086                     cls: "modal-dialog " + size,
4087                     cn : [
4088                         {
4089                             cls : "modal-content",
4090                             cn : [
4091                                 {
4092                                     cls : 'modal-header',
4093                                     cn : header
4094                                 },
4095                                 bdy,
4096                                 footer
4097                             ]
4098
4099                         }
4100                     ]
4101
4102                 }
4103             ]
4104         };
4105
4106         if(this.animate){
4107             modal.cls += ' fade';
4108         }
4109
4110         return modal;
4111
4112     },
4113     getChildContainer : function() {
4114
4115          return this.bodyEl;
4116
4117     },
4118     getButtonContainer : function() {
4119         
4120          return Roo.bootstrap.version == 4 ?
4121             this.el.select('.modal-footer',true).first()
4122             : this.el.select('.modal-footer div',true).first();
4123
4124     },
4125     initEvents : function()
4126     {
4127         if (this.allow_close) {
4128             this.closeEl.on('click', this.hide, this);
4129         }
4130         Roo.EventManager.onWindowResize(this.resize, this, true);
4131         if (this.editableTitle) {
4132             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4133             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4134             this.headerEditEl.on('keyup', function(e) {
4135                     if(e.isNavKeyPress()){
4136                             this.toggleHeaderInput(false)
4137                     }
4138                 }, this);
4139             this.headerEditEl.on('blur', function(e) {
4140                 this.toggleHeaderInput(false)
4141             },this);
4142         }
4143
4144     },
4145   
4146
4147     resize : function()
4148     {
4149         this.maskEl.setSize(
4150             Roo.lib.Dom.getViewWidth(true),
4151             Roo.lib.Dom.getViewHeight(true)
4152         );
4153         
4154         if (this.fitwindow) {
4155             
4156            
4157             this.setSize(
4158                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4159                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4160             );
4161             return;
4162         }
4163         
4164         if(this.max_width !== 0) {
4165             
4166             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4167             
4168             if(this.height) {
4169                 this.setSize(w, this.height);
4170                 return;
4171             }
4172             
4173             if(this.max_height) {
4174                 this.setSize(w,Math.min(
4175                     this.max_height,
4176                     Roo.lib.Dom.getViewportHeight(true) - 60
4177                 ));
4178                 
4179                 return;
4180             }
4181             
4182             if(!this.fit_content) {
4183                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4184                 return;
4185             }
4186             
4187             this.setSize(w, Math.min(
4188                 60 +
4189                 this.headerEl.getHeight() + 
4190                 this.footerEl.getHeight() + 
4191                 this.getChildHeight(this.bodyEl.dom.childNodes),
4192                 Roo.lib.Dom.getViewportHeight(true) - 60)
4193             );
4194         }
4195         
4196     },
4197
4198     setSize : function(w,h)
4199     {
4200         if (!w && !h) {
4201             return;
4202         }
4203         
4204         this.resizeTo(w,h);
4205     },
4206
4207     show : function() {
4208
4209         if (!this.rendered) {
4210             this.render();
4211         }
4212
4213         //this.el.setStyle('display', 'block');
4214         this.el.removeClass('hideing');
4215         this.el.dom.style.display='block';
4216         
4217         Roo.get(document.body).addClass('modal-open');
4218  
4219         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4220             
4221             (function(){
4222                 this.el.addClass('show');
4223                 this.el.addClass('in');
4224             }).defer(50, this);
4225         }else{
4226             this.el.addClass('show');
4227             this.el.addClass('in');
4228         }
4229
4230         // not sure how we can show data in here..
4231         //if (this.tmpl) {
4232         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4233         //}
4234
4235         Roo.get(document.body).addClass("x-body-masked");
4236         
4237         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4238         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4239         this.maskEl.dom.style.display = 'block';
4240         this.maskEl.addClass('show');
4241         
4242         
4243         this.resize();
4244         
4245         this.fireEvent('show', this);
4246
4247         // set zindex here - otherwise it appears to be ignored...
4248         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4249
4250         (function () {
4251             this.items.forEach( function(e) {
4252                 e.layout ? e.layout() : false;
4253
4254             });
4255         }).defer(100,this);
4256
4257     },
4258     hide : function()
4259     {
4260         if(this.fireEvent("beforehide", this) !== false){
4261             
4262             this.maskEl.removeClass('show');
4263             
4264             this.maskEl.dom.style.display = '';
4265             Roo.get(document.body).removeClass("x-body-masked");
4266             this.el.removeClass('in');
4267             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4268
4269             if(this.animate){ // why
4270                 this.el.addClass('hideing');
4271                 this.el.removeClass('show');
4272                 (function(){
4273                     if (!this.el.hasClass('hideing')) {
4274                         return; // it's been shown again...
4275                     }
4276                     
4277                     this.el.dom.style.display='';
4278
4279                     Roo.get(document.body).removeClass('modal-open');
4280                     this.el.removeClass('hideing');
4281                 }).defer(150,this);
4282                 
4283             }else{
4284                 this.el.removeClass('show');
4285                 this.el.dom.style.display='';
4286                 Roo.get(document.body).removeClass('modal-open');
4287
4288             }
4289             this.fireEvent('hide', this);
4290         }
4291     },
4292     isVisible : function()
4293     {
4294         
4295         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4296         
4297     },
4298
4299     addButton : function(str, cb)
4300     {
4301
4302
4303         var b = Roo.apply({}, { html : str } );
4304         b.xns = b.xns || Roo.bootstrap;
4305         b.xtype = b.xtype || 'Button';
4306         if (typeof(b.listeners) == 'undefined') {
4307             b.listeners = { click : cb.createDelegate(this)  };
4308         }
4309
4310         var btn = Roo.factory(b);
4311
4312         btn.render(this.getButtonContainer());
4313
4314         return btn;
4315
4316     },
4317
4318     setDefaultButton : function(btn)
4319     {
4320         //this.el.select('.modal-footer').()
4321     },
4322
4323     resizeTo: function(w,h)
4324     {
4325         this.dialogEl.setWidth(w);
4326         
4327         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4328
4329         this.bodyEl.setHeight(h - diff);
4330         
4331         this.fireEvent('resize', this);
4332     },
4333     
4334     setContentSize  : function(w, h)
4335     {
4336
4337     },
4338     onButtonClick: function(btn,e)
4339     {
4340         //Roo.log([a,b,c]);
4341         this.fireEvent('btnclick', btn.name, e);
4342     },
4343      /**
4344      * Set the title of the Dialog
4345      * @param {String} str new Title
4346      */
4347     setTitle: function(str) {
4348         this.titleEl.dom.innerHTML = str;
4349         this.title = str;
4350     },
4351     /**
4352      * Set the body of the Dialog
4353      * @param {String} str new Title
4354      */
4355     setBody: function(str) {
4356         this.bodyEl.dom.innerHTML = str;
4357     },
4358     /**
4359      * Set the body of the Dialog using the template
4360      * @param {Obj} data - apply this data to the template and replace the body contents.
4361      */
4362     applyBody: function(obj)
4363     {
4364         if (!this.tmpl) {
4365             Roo.log("Error - using apply Body without a template");
4366             //code
4367         }
4368         this.tmpl.overwrite(this.bodyEl, obj);
4369     },
4370     
4371     getChildHeight : function(child_nodes)
4372     {
4373         if(
4374             !child_nodes ||
4375             child_nodes.length == 0
4376         ) {
4377             return 0;
4378         }
4379         
4380         var child_height = 0;
4381         
4382         for(var i = 0; i < child_nodes.length; i++) {
4383             
4384             /*
4385             * for modal with tabs...
4386             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4387                 
4388                 var layout_childs = child_nodes[i].childNodes;
4389                 
4390                 for(var j = 0; j < layout_childs.length; j++) {
4391                     
4392                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4393                         
4394                         var layout_body_childs = layout_childs[j].childNodes;
4395                         
4396                         for(var k = 0; k < layout_body_childs.length; k++) {
4397                             
4398                             if(layout_body_childs[k].classList.contains('navbar')) {
4399                                 child_height += layout_body_childs[k].offsetHeight;
4400                                 continue;
4401                             }
4402                             
4403                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4404                                 
4405                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4406                                 
4407                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4408                                     
4409                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4410                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4411                                         continue;
4412                                     }
4413                                     
4414                                 }
4415                                 
4416                             }
4417                             
4418                         }
4419                     }
4420                 }
4421                 continue;
4422             }
4423             */
4424             
4425             child_height += child_nodes[i].offsetHeight;
4426             // Roo.log(child_nodes[i].offsetHeight);
4427         }
4428         
4429         return child_height;
4430     },
4431     toggleHeaderInput : function(is_edit)
4432     {
4433         
4434         if (is_edit && this.is_header_editing) {
4435             return; // already editing..
4436         }
4437         if (is_edit) {
4438     
4439             this.headerEditEl.dom.value = this.title;
4440             this.headerEditEl.removeClass('d-none');
4441             this.headerEditEl.dom.focus();
4442             this.titleEl.addClass('d-none');
4443             
4444             this.is_header_editing = true;
4445             return
4446         }
4447         // flip back to not editing.
4448         this.title = this.headerEditEl.dom.value;
4449         this.headerEditEl.addClass('d-none');
4450         this.titleEl.removeClass('d-none');
4451         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4452         this.is_header_editing = false;
4453         this.fireEvent('titlechanged', this, this.title);
4454     
4455             
4456         
4457     }
4458
4459 });
4460
4461
4462 Roo.apply(Roo.bootstrap.Modal,  {
4463     /**
4464          * Button config that displays a single OK button
4465          * @type Object
4466          */
4467         OK :  [{
4468             name : 'ok',
4469             weight : 'primary',
4470             html : 'OK'
4471         }],
4472         /**
4473          * Button config that displays Yes and No buttons
4474          * @type Object
4475          */
4476         YESNO : [
4477             {
4478                 name  : 'no',
4479                 html : 'No'
4480             },
4481             {
4482                 name  :'yes',
4483                 weight : 'primary',
4484                 html : 'Yes'
4485             }
4486         ],
4487
4488         /**
4489          * Button config that displays OK and Cancel buttons
4490          * @type Object
4491          */
4492         OKCANCEL : [
4493             {
4494                name : 'cancel',
4495                 html : 'Cancel'
4496             },
4497             {
4498                 name : 'ok',
4499                 weight : 'primary',
4500                 html : 'OK'
4501             }
4502         ],
4503         /**
4504          * Button config that displays Yes, No and Cancel buttons
4505          * @type Object
4506          */
4507         YESNOCANCEL : [
4508             {
4509                 name : 'yes',
4510                 weight : 'primary',
4511                 html : 'Yes'
4512             },
4513             {
4514                 name : 'no',
4515                 html : 'No'
4516             },
4517             {
4518                 name : 'cancel',
4519                 html : 'Cancel'
4520             }
4521         ],
4522         
4523         zIndex : 10001
4524 });
4525
4526 /*
4527  * - LGPL
4528  *
4529  * messagebox - can be used as a replace
4530  * 
4531  */
4532 /**
4533  * @class Roo.MessageBox
4534  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4535  * Example usage:
4536  *<pre><code>
4537 // Basic alert:
4538 Roo.Msg.alert('Status', 'Changes saved successfully.');
4539
4540 // Prompt for user data:
4541 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4542     if (btn == 'ok'){
4543         // process text value...
4544     }
4545 });
4546
4547 // Show a dialog using config options:
4548 Roo.Msg.show({
4549    title:'Save Changes?',
4550    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4551    buttons: Roo.Msg.YESNOCANCEL,
4552    fn: processResult,
4553    animEl: 'elId'
4554 });
4555 </code></pre>
4556  * @singleton
4557  */
4558 Roo.bootstrap.MessageBox = function(){
4559     var dlg, opt, mask, waitTimer;
4560     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4561     var buttons, activeTextEl, bwidth;
4562
4563     
4564     // private
4565     var handleButton = function(button){
4566         dlg.hide();
4567         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4568     };
4569
4570     // private
4571     var handleHide = function(){
4572         if(opt && opt.cls){
4573             dlg.el.removeClass(opt.cls);
4574         }
4575         //if(waitTimer){
4576         //    Roo.TaskMgr.stop(waitTimer);
4577         //    waitTimer = null;
4578         //}
4579     };
4580
4581     // private
4582     var updateButtons = function(b){
4583         var width = 0;
4584         if(!b){
4585             buttons["ok"].hide();
4586             buttons["cancel"].hide();
4587             buttons["yes"].hide();
4588             buttons["no"].hide();
4589             dlg.footerEl.hide();
4590             
4591             return width;
4592         }
4593         dlg.footerEl.show();
4594         for(var k in buttons){
4595             if(typeof buttons[k] != "function"){
4596                 if(b[k]){
4597                     buttons[k].show();
4598                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4599                     width += buttons[k].el.getWidth()+15;
4600                 }else{
4601                     buttons[k].hide();
4602                 }
4603             }
4604         }
4605         return width;
4606     };
4607
4608     // private
4609     var handleEsc = function(d, k, e){
4610         if(opt && opt.closable !== false){
4611             dlg.hide();
4612         }
4613         if(e){
4614             e.stopEvent();
4615         }
4616     };
4617
4618     return {
4619         /**
4620          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4621          * @return {Roo.BasicDialog} The BasicDialog element
4622          */
4623         getDialog : function(){
4624            if(!dlg){
4625                 dlg = new Roo.bootstrap.Modal( {
4626                     //draggable: true,
4627                     //resizable:false,
4628                     //constraintoviewport:false,
4629                     //fixedcenter:true,
4630                     //collapsible : false,
4631                     //shim:true,
4632                     //modal: true,
4633                 //    width: 'auto',
4634                   //  height:100,
4635                     //buttonAlign:"center",
4636                     closeClick : function(){
4637                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4638                             handleButton("no");
4639                         }else{
4640                             handleButton("cancel");
4641                         }
4642                     }
4643                 });
4644                 dlg.render();
4645                 dlg.on("hide", handleHide);
4646                 mask = dlg.mask;
4647                 //dlg.addKeyListener(27, handleEsc);
4648                 buttons = {};
4649                 this.buttons = buttons;
4650                 var bt = this.buttonText;
4651                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4652                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4653                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4654                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4655                 //Roo.log(buttons);
4656                 bodyEl = dlg.bodyEl.createChild({
4657
4658                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4659                         '<textarea class="roo-mb-textarea"></textarea>' +
4660                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4661                 });
4662                 msgEl = bodyEl.dom.firstChild;
4663                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4664                 textboxEl.enableDisplayMode();
4665                 textboxEl.addKeyListener([10,13], function(){
4666                     if(dlg.isVisible() && opt && opt.buttons){
4667                         if(opt.buttons.ok){
4668                             handleButton("ok");
4669                         }else if(opt.buttons.yes){
4670                             handleButton("yes");
4671                         }
4672                     }
4673                 });
4674                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4675                 textareaEl.enableDisplayMode();
4676                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4677                 progressEl.enableDisplayMode();
4678                 
4679                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4680                 var pf = progressEl.dom.firstChild;
4681                 if (pf) {
4682                     pp = Roo.get(pf.firstChild);
4683                     pp.setHeight(pf.offsetHeight);
4684                 }
4685                 
4686             }
4687             return dlg;
4688         },
4689
4690         /**
4691          * Updates the message box body text
4692          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4693          * the XHTML-compliant non-breaking space character '&amp;#160;')
4694          * @return {Roo.MessageBox} This message box
4695          */
4696         updateText : function(text)
4697         {
4698             if(!dlg.isVisible() && !opt.width){
4699                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4700                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4701             }
4702             msgEl.innerHTML = text || '&#160;';
4703       
4704             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4705             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4706             var w = Math.max(
4707                     Math.min(opt.width || cw , this.maxWidth), 
4708                     Math.max(opt.minWidth || this.minWidth, bwidth)
4709             );
4710             if(opt.prompt){
4711                 activeTextEl.setWidth(w);
4712             }
4713             if(dlg.isVisible()){
4714                 dlg.fixedcenter = false;
4715             }
4716             // to big, make it scroll. = But as usual stupid IE does not support
4717             // !important..
4718             
4719             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4720                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4721                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4722             } else {
4723                 bodyEl.dom.style.height = '';
4724                 bodyEl.dom.style.overflowY = '';
4725             }
4726             if (cw > w) {
4727                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4728             } else {
4729                 bodyEl.dom.style.overflowX = '';
4730             }
4731             
4732             dlg.setContentSize(w, bodyEl.getHeight());
4733             if(dlg.isVisible()){
4734                 dlg.fixedcenter = true;
4735             }
4736             return this;
4737         },
4738
4739         /**
4740          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4741          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4742          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4743          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4744          * @return {Roo.MessageBox} This message box
4745          */
4746         updateProgress : function(value, text){
4747             if(text){
4748                 this.updateText(text);
4749             }
4750             
4751             if (pp) { // weird bug on my firefox - for some reason this is not defined
4752                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4753                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4754             }
4755             return this;
4756         },        
4757
4758         /**
4759          * Returns true if the message box is currently displayed
4760          * @return {Boolean} True if the message box is visible, else false
4761          */
4762         isVisible : function(){
4763             return dlg && dlg.isVisible();  
4764         },
4765
4766         /**
4767          * Hides the message box if it is displayed
4768          */
4769         hide : function(){
4770             if(this.isVisible()){
4771                 dlg.hide();
4772             }  
4773         },
4774
4775         /**
4776          * Displays a new message box, or reinitializes an existing message box, based on the config options
4777          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4778          * The following config object properties are supported:
4779          * <pre>
4780 Property    Type             Description
4781 ----------  ---------------  ------------------------------------------------------------------------------------
4782 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4783                                    closes (defaults to undefined)
4784 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4785                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4786 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4787                                    progress and wait dialogs will ignore this property and always hide the
4788                                    close button as they can only be closed programmatically.
4789 cls               String           A custom CSS class to apply to the message box element
4790 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4791                                    displayed (defaults to 75)
4792 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4793                                    function will be btn (the name of the button that was clicked, if applicable,
4794                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4795                                    Progress and wait dialogs will ignore this option since they do not respond to
4796                                    user actions and can only be closed programmatically, so any required function
4797                                    should be called by the same code after it closes the dialog.
4798 icon              String           A CSS class that provides a background image to be used as an icon for
4799                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4800 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4801 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4802 modal             Boolean          False to allow user interaction with the page while the message box is
4803                                    displayed (defaults to true)
4804 msg               String           A string that will replace the existing message box body text (defaults
4805                                    to the XHTML-compliant non-breaking space character '&#160;')
4806 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4807 progress          Boolean          True to display a progress bar (defaults to false)
4808 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4809 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4810 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4811 title             String           The title text
4812 value             String           The string value to set into the active textbox element if displayed
4813 wait              Boolean          True to display a progress bar (defaults to false)
4814 width             Number           The width of the dialog in pixels
4815 </pre>
4816          *
4817          * Example usage:
4818          * <pre><code>
4819 Roo.Msg.show({
4820    title: 'Address',
4821    msg: 'Please enter your address:',
4822    width: 300,
4823    buttons: Roo.MessageBox.OKCANCEL,
4824    multiline: true,
4825    fn: saveAddress,
4826    animEl: 'addAddressBtn'
4827 });
4828 </code></pre>
4829          * @param {Object} config Configuration options
4830          * @return {Roo.MessageBox} This message box
4831          */
4832         show : function(options)
4833         {
4834             
4835             // this causes nightmares if you show one dialog after another
4836             // especially on callbacks..
4837              
4838             if(this.isVisible()){
4839                 
4840                 this.hide();
4841                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4842                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4843                 Roo.log("New Dialog Message:" +  options.msg )
4844                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4845                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4846                 
4847             }
4848             var d = this.getDialog();
4849             opt = options;
4850             d.setTitle(opt.title || "&#160;");
4851             d.closeEl.setDisplayed(opt.closable !== false);
4852             activeTextEl = textboxEl;
4853             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4854             if(opt.prompt){
4855                 if(opt.multiline){
4856                     textboxEl.hide();
4857                     textareaEl.show();
4858                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4859                         opt.multiline : this.defaultTextHeight);
4860                     activeTextEl = textareaEl;
4861                 }else{
4862                     textboxEl.show();
4863                     textareaEl.hide();
4864                 }
4865             }else{
4866                 textboxEl.hide();
4867                 textareaEl.hide();
4868             }
4869             progressEl.setDisplayed(opt.progress === true);
4870             if (opt.progress) {
4871                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4872             }
4873             this.updateProgress(0);
4874             activeTextEl.dom.value = opt.value || "";
4875             if(opt.prompt){
4876                 dlg.setDefaultButton(activeTextEl);
4877             }else{
4878                 var bs = opt.buttons;
4879                 var db = null;
4880                 if(bs && bs.ok){
4881                     db = buttons["ok"];
4882                 }else if(bs && bs.yes){
4883                     db = buttons["yes"];
4884                 }
4885                 dlg.setDefaultButton(db);
4886             }
4887             bwidth = updateButtons(opt.buttons);
4888             this.updateText(opt.msg);
4889             if(opt.cls){
4890                 d.el.addClass(opt.cls);
4891             }
4892             d.proxyDrag = opt.proxyDrag === true;
4893             d.modal = opt.modal !== false;
4894             d.mask = opt.modal !== false ? mask : false;
4895             if(!d.isVisible()){
4896                 // force it to the end of the z-index stack so it gets a cursor in FF
4897                 document.body.appendChild(dlg.el.dom);
4898                 d.animateTarget = null;
4899                 d.show(options.animEl);
4900             }
4901             return this;
4902         },
4903
4904         /**
4905          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
4906          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
4907          * and closing the message box when the process is complete.
4908          * @param {String} title The title bar text
4909          * @param {String} msg The message box body text
4910          * @return {Roo.MessageBox} This message box
4911          */
4912         progress : function(title, msg){
4913             this.show({
4914                 title : title,
4915                 msg : msg,
4916                 buttons: false,
4917                 progress:true,
4918                 closable:false,
4919                 minWidth: this.minProgressWidth,
4920                 modal : true
4921             });
4922             return this;
4923         },
4924
4925         /**
4926          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
4927          * If a callback function is passed it will be called after the user clicks the button, and the
4928          * id of the button that was clicked will be passed as the only parameter to the callback
4929          * (could also be the top-right close button).
4930          * @param {String} title The title bar text
4931          * @param {String} msg The message box body text
4932          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4933          * @param {Object} scope (optional) The scope of the callback function
4934          * @return {Roo.MessageBox} This message box
4935          */
4936         alert : function(title, msg, fn, scope)
4937         {
4938             this.show({
4939                 title : title,
4940                 msg : msg,
4941                 buttons: this.OK,
4942                 fn: fn,
4943                 closable : false,
4944                 scope : scope,
4945                 modal : true
4946             });
4947             return this;
4948         },
4949
4950         /**
4951          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
4952          * interaction while waiting for a long-running process to complete that does not have defined intervals.
4953          * You are responsible for closing the message box when the process is complete.
4954          * @param {String} msg The message box body text
4955          * @param {String} title (optional) The title bar text
4956          * @return {Roo.MessageBox} This message box
4957          */
4958         wait : function(msg, title){
4959             this.show({
4960                 title : title,
4961                 msg : msg,
4962                 buttons: false,
4963                 closable:false,
4964                 progress:true,
4965                 modal:true,
4966                 width:300,
4967                 wait:true
4968             });
4969             waitTimer = Roo.TaskMgr.start({
4970                 run: function(i){
4971                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
4972                 },
4973                 interval: 1000
4974             });
4975             return this;
4976         },
4977
4978         /**
4979          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
4980          * If a callback function is passed it will be called after the user clicks either button, and the id of the
4981          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
4982          * @param {String} title The title bar text
4983          * @param {String} msg The message box body text
4984          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4985          * @param {Object} scope (optional) The scope of the callback function
4986          * @return {Roo.MessageBox} This message box
4987          */
4988         confirm : function(title, msg, fn, scope){
4989             this.show({
4990                 title : title,
4991                 msg : msg,
4992                 buttons: this.YESNO,
4993                 fn: fn,
4994                 scope : scope,
4995                 modal : true
4996             });
4997             return this;
4998         },
4999
5000         /**
5001          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5002          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5003          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5004          * (could also be the top-right close button) and the text that was entered will be passed as the two
5005          * parameters to the callback.
5006          * @param {String} title The title bar text
5007          * @param {String} msg The message box body text
5008          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5009          * @param {Object} scope (optional) The scope of the callback function
5010          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5011          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5012          * @return {Roo.MessageBox} This message box
5013          */
5014         prompt : function(title, msg, fn, scope, multiline){
5015             this.show({
5016                 title : title,
5017                 msg : msg,
5018                 buttons: this.OKCANCEL,
5019                 fn: fn,
5020                 minWidth:250,
5021                 scope : scope,
5022                 prompt:true,
5023                 multiline: multiline,
5024                 modal : true
5025             });
5026             return this;
5027         },
5028
5029         /**
5030          * Button config that displays a single OK button
5031          * @type Object
5032          */
5033         OK : {ok:true},
5034         /**
5035          * Button config that displays Yes and No buttons
5036          * @type Object
5037          */
5038         YESNO : {yes:true, no:true},
5039         /**
5040          * Button config that displays OK and Cancel buttons
5041          * @type Object
5042          */
5043         OKCANCEL : {ok:true, cancel:true},
5044         /**
5045          * Button config that displays Yes, No and Cancel buttons
5046          * @type Object
5047          */
5048         YESNOCANCEL : {yes:true, no:true, cancel:true},
5049
5050         /**
5051          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5052          * @type Number
5053          */
5054         defaultTextHeight : 75,
5055         /**
5056          * The maximum width in pixels of the message box (defaults to 600)
5057          * @type Number
5058          */
5059         maxWidth : 600,
5060         /**
5061          * The minimum width in pixels of the message box (defaults to 100)
5062          * @type Number
5063          */
5064         minWidth : 100,
5065         /**
5066          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5067          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5068          * @type Number
5069          */
5070         minProgressWidth : 250,
5071         /**
5072          * An object containing the default button text strings that can be overriden for localized language support.
5073          * Supported properties are: ok, cancel, yes and no.
5074          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5075          * @type Object
5076          */
5077         buttonText : {
5078             ok : "OK",
5079             cancel : "Cancel",
5080             yes : "Yes",
5081             no : "No"
5082         }
5083     };
5084 }();
5085
5086 /**
5087  * Shorthand for {@link Roo.MessageBox}
5088  */
5089 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5090 Roo.Msg = Roo.Msg || Roo.MessageBox;
5091 /*
5092  * - LGPL
5093  *
5094  * navbar
5095  * 
5096  */
5097
5098 /**
5099  * @class Roo.bootstrap.Navbar
5100  * @extends Roo.bootstrap.Component
5101  * Bootstrap Navbar class
5102
5103  * @constructor
5104  * Create a new Navbar
5105  * @param {Object} config The config object
5106  */
5107
5108
5109 Roo.bootstrap.Navbar = function(config){
5110     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5111     this.addEvents({
5112         // raw events
5113         /**
5114          * @event beforetoggle
5115          * Fire before toggle the menu
5116          * @param {Roo.EventObject} e
5117          */
5118         "beforetoggle" : true
5119     });
5120 };
5121
5122 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5123     
5124     
5125    
5126     // private
5127     navItems : false,
5128     loadMask : false,
5129     
5130     
5131     getAutoCreate : function(){
5132         
5133         
5134         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5135         
5136     },
5137     
5138     initEvents :function ()
5139     {
5140         //Roo.log(this.el.select('.navbar-toggle',true));
5141         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5142         
5143         var mark = {
5144             tag: "div",
5145             cls:"x-dlg-mask"
5146         };
5147         
5148         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5149         
5150         var size = this.el.getSize();
5151         this.maskEl.setSize(size.width, size.height);
5152         this.maskEl.enableDisplayMode("block");
5153         this.maskEl.hide();
5154         
5155         if(this.loadMask){
5156             this.maskEl.show();
5157         }
5158     },
5159     
5160     
5161     getChildContainer : function()
5162     {
5163         if (this.el && this.el.select('.collapse').getCount()) {
5164             return this.el.select('.collapse',true).first();
5165         }
5166         
5167         return this.el;
5168     },
5169     
5170     mask : function()
5171     {
5172         this.maskEl.show();
5173     },
5174     
5175     unmask : function()
5176     {
5177         this.maskEl.hide();
5178     },
5179     onToggle : function()
5180     {
5181         
5182         if(this.fireEvent('beforetoggle', this) === false){
5183             return;
5184         }
5185         var ce = this.el.select('.navbar-collapse',true).first();
5186       
5187         if (!ce.hasClass('show')) {
5188            this.expand();
5189         } else {
5190             this.collapse();
5191         }
5192         
5193         
5194     
5195     },
5196     /**
5197      * Expand the navbar pulldown 
5198      */
5199     expand : function ()
5200     {
5201        
5202         var ce = this.el.select('.navbar-collapse',true).first();
5203         if (ce.hasClass('collapsing')) {
5204             return;
5205         }
5206         ce.dom.style.height = '';
5207                // show it...
5208         ce.addClass('in'); // old...
5209         ce.removeClass('collapse');
5210         ce.addClass('show');
5211         var h = ce.getHeight();
5212         Roo.log(h);
5213         ce.removeClass('show');
5214         // at this point we should be able to see it..
5215         ce.addClass('collapsing');
5216         
5217         ce.setHeight(0); // resize it ...
5218         ce.on('transitionend', function() {
5219             //Roo.log('done transition');
5220             ce.removeClass('collapsing');
5221             ce.addClass('show');
5222             ce.removeClass('collapse');
5223
5224             ce.dom.style.height = '';
5225         }, this, { single: true} );
5226         ce.setHeight(h);
5227         ce.dom.scrollTop = 0;
5228     },
5229     /**
5230      * Collapse the navbar pulldown 
5231      */
5232     collapse : function()
5233     {
5234          var ce = this.el.select('.navbar-collapse',true).first();
5235        
5236         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5237             // it's collapsed or collapsing..
5238             return;
5239         }
5240         ce.removeClass('in'); // old...
5241         ce.setHeight(ce.getHeight());
5242         ce.removeClass('show');
5243         ce.addClass('collapsing');
5244         
5245         ce.on('transitionend', function() {
5246             ce.dom.style.height = '';
5247             ce.removeClass('collapsing');
5248             ce.addClass('collapse');
5249         }, this, { single: true} );
5250         ce.setHeight(0);
5251     }
5252     
5253     
5254     
5255 });
5256
5257
5258
5259  
5260
5261  /*
5262  * - LGPL
5263  *
5264  * navbar
5265  * 
5266  */
5267
5268 /**
5269  * @class Roo.bootstrap.NavSimplebar
5270  * @extends Roo.bootstrap.Navbar
5271  * Bootstrap Sidebar class
5272  *
5273  * @cfg {Boolean} inverse is inverted color
5274  * 
5275  * @cfg {String} type (nav | pills | tabs)
5276  * @cfg {Boolean} arrangement stacked | justified
5277  * @cfg {String} align (left | right) alignment
5278  * 
5279  * @cfg {Boolean} main (true|false) main nav bar? default false
5280  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5281  * 
5282  * @cfg {String} tag (header|footer|nav|div) default is nav 
5283
5284  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5285  * 
5286  * 
5287  * @constructor
5288  * Create a new Sidebar
5289  * @param {Object} config The config object
5290  */
5291
5292
5293 Roo.bootstrap.NavSimplebar = function(config){
5294     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5295 };
5296
5297 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5298     
5299     inverse: false,
5300     
5301     type: false,
5302     arrangement: '',
5303     align : false,
5304     
5305     weight : 'light',
5306     
5307     main : false,
5308     
5309     
5310     tag : false,
5311     
5312     
5313     getAutoCreate : function(){
5314         
5315         
5316         var cfg = {
5317             tag : this.tag || 'div',
5318             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5319         };
5320         if (['light','white'].indexOf(this.weight) > -1) {
5321             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5322         }
5323         cfg.cls += ' bg-' + this.weight;
5324         
5325         if (this.inverse) {
5326             cfg.cls += ' navbar-inverse';
5327             
5328         }
5329         
5330         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5331         
5332         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5333             return cfg;
5334         }
5335         
5336         
5337     
5338         
5339         cfg.cn = [
5340             {
5341                 cls: 'nav nav-' + this.xtype,
5342                 tag : 'ul'
5343             }
5344         ];
5345         
5346          
5347         this.type = this.type || 'nav';
5348         if (['tabs','pills'].indexOf(this.type) != -1) {
5349             cfg.cn[0].cls += ' nav-' + this.type
5350         
5351         
5352         } else {
5353             if (this.type!=='nav') {
5354                 Roo.log('nav type must be nav/tabs/pills')
5355             }
5356             cfg.cn[0].cls += ' navbar-nav'
5357         }
5358         
5359         
5360         
5361         
5362         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5363             cfg.cn[0].cls += ' nav-' + this.arrangement;
5364         }
5365         
5366         
5367         if (this.align === 'right') {
5368             cfg.cn[0].cls += ' navbar-right';
5369         }
5370         
5371         
5372         
5373         
5374         return cfg;
5375     
5376         
5377     }
5378     
5379     
5380     
5381 });
5382
5383
5384
5385  
5386
5387  
5388        /*
5389  * - LGPL
5390  *
5391  * navbar
5392  * navbar-fixed-top
5393  * navbar-expand-md  fixed-top 
5394  */
5395
5396 /**
5397  * @class Roo.bootstrap.NavHeaderbar
5398  * @extends Roo.bootstrap.NavSimplebar
5399  * Bootstrap Sidebar class
5400  *
5401  * @cfg {String} brand what is brand
5402  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5403  * @cfg {String} brand_href href of the brand
5404  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5405  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5406  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5407  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5408  * 
5409  * @constructor
5410  * Create a new Sidebar
5411  * @param {Object} config The config object
5412  */
5413
5414
5415 Roo.bootstrap.NavHeaderbar = function(config){
5416     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5417       
5418 };
5419
5420 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5421     
5422     position: '',
5423     brand: '',
5424     brand_href: false,
5425     srButton : true,
5426     autohide : false,
5427     desktopCenter : false,
5428    
5429     
5430     getAutoCreate : function(){
5431         
5432         var   cfg = {
5433             tag: this.nav || 'nav',
5434             cls: 'navbar navbar-expand-md',
5435             role: 'navigation',
5436             cn: []
5437         };
5438         
5439         var cn = cfg.cn;
5440         if (this.desktopCenter) {
5441             cn.push({cls : 'container', cn : []});
5442             cn = cn[0].cn;
5443         }
5444         
5445         if(this.srButton){
5446             var btn = {
5447                 tag: 'button',
5448                 type: 'button',
5449                 cls: 'navbar-toggle navbar-toggler',
5450                 'data-toggle': 'collapse',
5451                 cn: [
5452                     {
5453                         tag: 'span',
5454                         cls: 'sr-only',
5455                         html: 'Toggle navigation'
5456                     },
5457                     {
5458                         tag: 'span',
5459                         cls: 'icon-bar navbar-toggler-icon'
5460                     },
5461                     {
5462                         tag: 'span',
5463                         cls: 'icon-bar'
5464                     },
5465                     {
5466                         tag: 'span',
5467                         cls: 'icon-bar'
5468                     }
5469                 ]
5470             };
5471             
5472             cn.push( Roo.bootstrap.version == 4 ? btn : {
5473                 tag: 'div',
5474                 cls: 'navbar-header',
5475                 cn: [
5476                     btn
5477                 ]
5478             });
5479         }
5480         
5481         cn.push({
5482             tag: 'div',
5483             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5484             cn : []
5485         });
5486         
5487         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5488         
5489         if (['light','white'].indexOf(this.weight) > -1) {
5490             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5491         }
5492         cfg.cls += ' bg-' + this.weight;
5493         
5494         
5495         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5496             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5497             
5498             // tag can override this..
5499             
5500             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5501         }
5502         
5503         if (this.brand !== '') {
5504             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5505             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5506                 tag: 'a',
5507                 href: this.brand_href ? this.brand_href : '#',
5508                 cls: 'navbar-brand',
5509                 cn: [
5510                 this.brand
5511                 ]
5512             });
5513         }
5514         
5515         if(this.main){
5516             cfg.cls += ' main-nav';
5517         }
5518         
5519         
5520         return cfg;
5521
5522         
5523     },
5524     getHeaderChildContainer : function()
5525     {
5526         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5527             return this.el.select('.navbar-header',true).first();
5528         }
5529         
5530         return this.getChildContainer();
5531     },
5532     
5533     getChildContainer : function()
5534     {
5535          
5536         return this.el.select('.roo-navbar-collapse',true).first();
5537          
5538         
5539     },
5540     
5541     initEvents : function()
5542     {
5543         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5544         
5545         if (this.autohide) {
5546             
5547             var prevScroll = 0;
5548             var ft = this.el;
5549             
5550             Roo.get(document).on('scroll',function(e) {
5551                 var ns = Roo.get(document).getScroll().top;
5552                 var os = prevScroll;
5553                 prevScroll = ns;
5554                 
5555                 if(ns > os){
5556                     ft.removeClass('slideDown');
5557                     ft.addClass('slideUp');
5558                     return;
5559                 }
5560                 ft.removeClass('slideUp');
5561                 ft.addClass('slideDown');
5562                  
5563               
5564           },this);
5565         }
5566     }    
5567     
5568 });
5569
5570
5571
5572  
5573
5574  /*
5575  * - LGPL
5576  *
5577  * navbar
5578  * 
5579  */
5580
5581 /**
5582  * @class Roo.bootstrap.NavSidebar
5583  * @extends Roo.bootstrap.Navbar
5584  * Bootstrap Sidebar class
5585  * 
5586  * @constructor
5587  * Create a new Sidebar
5588  * @param {Object} config The config object
5589  */
5590
5591
5592 Roo.bootstrap.NavSidebar = function(config){
5593     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5594 };
5595
5596 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5597     
5598     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5599     
5600     getAutoCreate : function(){
5601         
5602         
5603         return  {
5604             tag: 'div',
5605             cls: 'sidebar sidebar-nav'
5606         };
5607     
5608         
5609     }
5610     
5611     
5612     
5613 });
5614
5615
5616
5617  
5618
5619  /*
5620  * - LGPL
5621  *
5622  * nav group
5623  * 
5624  */
5625
5626 /**
5627  * @class Roo.bootstrap.NavGroup
5628  * @extends Roo.bootstrap.Component
5629  * Bootstrap NavGroup class
5630  * @cfg {String} align (left|right)
5631  * @cfg {Boolean} inverse
5632  * @cfg {String} type (nav|pills|tab) default nav
5633  * @cfg {String} navId - reference Id for navbar.
5634
5635  * 
5636  * @constructor
5637  * Create a new nav group
5638  * @param {Object} config The config object
5639  */
5640
5641 Roo.bootstrap.NavGroup = function(config){
5642     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5643     this.navItems = [];
5644    
5645     Roo.bootstrap.NavGroup.register(this);
5646      this.addEvents({
5647         /**
5648              * @event changed
5649              * Fires when the active item changes
5650              * @param {Roo.bootstrap.NavGroup} this
5651              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5652              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5653          */
5654         'changed': true
5655      });
5656     
5657 };
5658
5659 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5660     
5661     align: '',
5662     inverse: false,
5663     form: false,
5664     type: 'nav',
5665     navId : '',
5666     // private
5667     
5668     navItems : false, 
5669     
5670     getAutoCreate : function()
5671     {
5672         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5673         
5674         cfg = {
5675             tag : 'ul',
5676             cls: 'nav' 
5677         };
5678         if (Roo.bootstrap.version == 4) {
5679             if (['tabs','pills'].indexOf(this.type) != -1) {
5680                 cfg.cls += ' nav-' + this.type; 
5681             } else {
5682                 // trying to remove so header bar can right align top?
5683                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5684                     // do not use on header bar... 
5685                     cfg.cls += ' navbar-nav';
5686                 }
5687             }
5688             
5689         } else {
5690             if (['tabs','pills'].indexOf(this.type) != -1) {
5691                 cfg.cls += ' nav-' + this.type
5692             } else {
5693                 if (this.type !== 'nav') {
5694                     Roo.log('nav type must be nav/tabs/pills')
5695                 }
5696                 cfg.cls += ' navbar-nav'
5697             }
5698         }
5699         
5700         if (this.parent() && this.parent().sidebar) {
5701             cfg = {
5702                 tag: 'ul',
5703                 cls: 'dashboard-menu sidebar-menu'
5704             };
5705             
5706             return cfg;
5707         }
5708         
5709         if (this.form === true) {
5710             cfg = {
5711                 tag: 'form',
5712                 cls: 'navbar-form form-inline'
5713             };
5714             //nav navbar-right ml-md-auto
5715             if (this.align === 'right') {
5716                 cfg.cls += ' navbar-right ml-md-auto';
5717             } else {
5718                 cfg.cls += ' navbar-left';
5719             }
5720         }
5721         
5722         if (this.align === 'right') {
5723             cfg.cls += ' navbar-right ml-md-auto';
5724         } else {
5725             cfg.cls += ' mr-auto';
5726         }
5727         
5728         if (this.inverse) {
5729             cfg.cls += ' navbar-inverse';
5730             
5731         }
5732         
5733         
5734         return cfg;
5735     },
5736     /**
5737     * sets the active Navigation item
5738     * @param {Roo.bootstrap.NavItem} the new current navitem
5739     */
5740     setActiveItem : function(item)
5741     {
5742         var prev = false;
5743         Roo.each(this.navItems, function(v){
5744             if (v == item) {
5745                 return ;
5746             }
5747             if (v.isActive()) {
5748                 v.setActive(false, true);
5749                 prev = v;
5750                 
5751             }
5752             
5753         });
5754
5755         item.setActive(true, true);
5756         this.fireEvent('changed', this, item, prev);
5757         
5758         
5759     },
5760     /**
5761     * gets the active Navigation item
5762     * @return {Roo.bootstrap.NavItem} the current navitem
5763     */
5764     getActive : function()
5765     {
5766         
5767         var prev = false;
5768         Roo.each(this.navItems, function(v){
5769             
5770             if (v.isActive()) {
5771                 prev = v;
5772                 
5773             }
5774             
5775         });
5776         return prev;
5777     },
5778     
5779     indexOfNav : function()
5780     {
5781         
5782         var prev = false;
5783         Roo.each(this.navItems, function(v,i){
5784             
5785             if (v.isActive()) {
5786                 prev = i;
5787                 
5788             }
5789             
5790         });
5791         return prev;
5792     },
5793     /**
5794     * adds a Navigation item
5795     * @param {Roo.bootstrap.NavItem} the navitem to add
5796     */
5797     addItem : function(cfg)
5798     {
5799         if (this.form && Roo.bootstrap.version == 4) {
5800             cfg.tag = 'div';
5801         }
5802         var cn = new Roo.bootstrap.NavItem(cfg);
5803         this.register(cn);
5804         cn.parentId = this.id;
5805         cn.onRender(this.el, null);
5806         return cn;
5807     },
5808     /**
5809     * register a Navigation item
5810     * @param {Roo.bootstrap.NavItem} the navitem to add
5811     */
5812     register : function(item)
5813     {
5814         this.navItems.push( item);
5815         item.navId = this.navId;
5816     
5817     },
5818     
5819     /**
5820     * clear all the Navigation item
5821     */
5822    
5823     clearAll : function()
5824     {
5825         this.navItems = [];
5826         this.el.dom.innerHTML = '';
5827     },
5828     
5829     getNavItem: function(tabId)
5830     {
5831         var ret = false;
5832         Roo.each(this.navItems, function(e) {
5833             if (e.tabId == tabId) {
5834                ret =  e;
5835                return false;
5836             }
5837             return true;
5838             
5839         });
5840         return ret;
5841     },
5842     
5843     setActiveNext : function()
5844     {
5845         var i = this.indexOfNav(this.getActive());
5846         if (i > this.navItems.length) {
5847             return;
5848         }
5849         this.setActiveItem(this.navItems[i+1]);
5850     },
5851     setActivePrev : function()
5852     {
5853         var i = this.indexOfNav(this.getActive());
5854         if (i  < 1) {
5855             return;
5856         }
5857         this.setActiveItem(this.navItems[i-1]);
5858     },
5859     clearWasActive : function(except) {
5860         Roo.each(this.navItems, function(e) {
5861             if (e.tabId != except.tabId && e.was_active) {
5862                e.was_active = false;
5863                return false;
5864             }
5865             return true;
5866             
5867         });
5868     },
5869     getWasActive : function ()
5870     {
5871         var r = false;
5872         Roo.each(this.navItems, function(e) {
5873             if (e.was_active) {
5874                r = e;
5875                return false;
5876             }
5877             return true;
5878             
5879         });
5880         return r;
5881     }
5882     
5883     
5884 });
5885
5886  
5887 Roo.apply(Roo.bootstrap.NavGroup, {
5888     
5889     groups: {},
5890      /**
5891     * register a Navigation Group
5892     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5893     */
5894     register : function(navgrp)
5895     {
5896         this.groups[navgrp.navId] = navgrp;
5897         
5898     },
5899     /**
5900     * fetch a Navigation Group based on the navigation ID
5901     * @param {string} the navgroup to add
5902     * @returns {Roo.bootstrap.NavGroup} the navgroup 
5903     */
5904     get: function(navId) {
5905         if (typeof(this.groups[navId]) == 'undefined') {
5906             return false;
5907             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
5908         }
5909         return this.groups[navId] ;
5910     }
5911     
5912     
5913     
5914 });
5915
5916  /*
5917  * - LGPL
5918  *
5919  * row
5920  * 
5921  */
5922
5923 /**
5924  * @class Roo.bootstrap.NavItem
5925  * @extends Roo.bootstrap.Component
5926  * Bootstrap Navbar.NavItem class
5927  * @cfg {String} href  link to
5928  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
5929
5930  * @cfg {String} html content of button
5931  * @cfg {String} badge text inside badge
5932  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
5933  * @cfg {String} glyphicon DEPRICATED - use fa
5934  * @cfg {String} icon DEPRICATED - use fa
5935  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
5936  * @cfg {Boolean} active Is item active
5937  * @cfg {Boolean} disabled Is item disabled
5938  
5939  * @cfg {Boolean} preventDefault (true | false) default false
5940  * @cfg {String} tabId the tab that this item activates.
5941  * @cfg {String} tagtype (a|span) render as a href or span?
5942  * @cfg {Boolean} animateRef (true|false) link to element default false  
5943   
5944  * @constructor
5945  * Create a new Navbar Item
5946  * @param {Object} config The config object
5947  */
5948 Roo.bootstrap.NavItem = function(config){
5949     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
5950     this.addEvents({
5951         // raw events
5952         /**
5953          * @event click
5954          * The raw click event for the entire grid.
5955          * @param {Roo.EventObject} e
5956          */
5957         "click" : true,
5958          /**
5959             * @event changed
5960             * Fires when the active item active state changes
5961             * @param {Roo.bootstrap.NavItem} this
5962             * @param {boolean} state the new state
5963              
5964          */
5965         'changed': true,
5966         /**
5967             * @event scrollto
5968             * Fires when scroll to element
5969             * @param {Roo.bootstrap.NavItem} this
5970             * @param {Object} options
5971             * @param {Roo.EventObject} e
5972              
5973          */
5974         'scrollto': true
5975     });
5976    
5977 };
5978
5979 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
5980     
5981     href: false,
5982     html: '',
5983     badge: '',
5984     icon: false,
5985     fa : false,
5986     glyphicon: false,
5987     active: false,
5988     preventDefault : false,
5989     tabId : false,
5990     tagtype : 'a',
5991     tag: 'li',
5992     disabled : false,
5993     animateRef : false,
5994     was_active : false,
5995     button_weight : '',
5996     button_outline : false,
5997     
5998     navLink: false,
5999     
6000     getAutoCreate : function(){
6001          
6002         var cfg = {
6003             tag: this.tag,
6004             cls: 'nav-item'
6005         };
6006         
6007         if (this.active) {
6008             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6009         }
6010         if (this.disabled) {
6011             cfg.cls += ' disabled';
6012         }
6013         
6014         // BS4 only?
6015         if (this.button_weight.length) {
6016             cfg.tag = this.href ? 'a' : 'button';
6017             cfg.html = this.html || '';
6018             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6019             if (this.href) {
6020                 cfg.href = this.href;
6021             }
6022             if (this.fa) {
6023                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6024             }
6025             
6026             // menu .. should add dropdown-menu class - so no need for carat..
6027             
6028             if (this.badge !== '') {
6029                  
6030                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6031             }
6032             return cfg;
6033         }
6034         
6035         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6036             cfg.cn = [
6037                 {
6038                     tag: this.tagtype,
6039                     href : this.href || "#",
6040                     html: this.html || ''
6041                 }
6042             ];
6043             if (this.tagtype == 'a') {
6044                 cfg.cn[0].cls = 'nav-link';
6045             }
6046             if (this.icon) {
6047                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6048             }
6049             if (this.fa) {
6050                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6051             }
6052             if(this.glyphicon) {
6053                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6054             }
6055             
6056             if (this.menu) {
6057                 
6058                 cfg.cn[0].html += " <span class='caret'></span>";
6059              
6060             }
6061             
6062             if (this.badge !== '') {
6063                  
6064                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6065             }
6066         }
6067         
6068         
6069         
6070         return cfg;
6071     },
6072     onRender : function(ct, position)
6073     {
6074        // Roo.log("Call onRender: " + this.xtype);
6075         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6076             this.tag = 'div';
6077         }
6078         
6079         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6080         this.navLink = this.el.select('.nav-link',true).first();
6081         return ret;
6082     },
6083       
6084     
6085     initEvents: function() 
6086     {
6087         if (typeof (this.menu) != 'undefined') {
6088             this.menu.parentType = this.xtype;
6089             this.menu.triggerEl = this.el;
6090             this.menu = this.addxtype(Roo.apply({}, this.menu));
6091         }
6092         
6093         this.el.select('a',true).on('click', this.onClick, this);
6094         
6095         if(this.tagtype == 'span'){
6096             this.el.select('span',true).on('click', this.onClick, this);
6097         }
6098        
6099         // at this point parent should be available..
6100         this.parent().register(this);
6101     },
6102     
6103     onClick : function(e)
6104     {
6105         if (e.getTarget('.dropdown-menu-item')) {
6106             // did you click on a menu itemm.... - then don't trigger onclick..
6107             return;
6108         }
6109         
6110         if(
6111                 this.preventDefault || 
6112                 this.href == '#' 
6113         ){
6114             Roo.log("NavItem - prevent Default?");
6115             e.preventDefault();
6116         }
6117         
6118         if (this.disabled) {
6119             return;
6120         }
6121         
6122         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6123         if (tg && tg.transition) {
6124             Roo.log("waiting for the transitionend");
6125             return;
6126         }
6127         
6128         
6129         
6130         //Roo.log("fire event clicked");
6131         if(this.fireEvent('click', this, e) === false){
6132             return;
6133         };
6134         
6135         if(this.tagtype == 'span'){
6136             return;
6137         }
6138         
6139         //Roo.log(this.href);
6140         var ael = this.el.select('a',true).first();
6141         //Roo.log(ael);
6142         
6143         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6144             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6145             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6146                 return; // ignore... - it's a 'hash' to another page.
6147             }
6148             Roo.log("NavItem - prevent Default?");
6149             e.preventDefault();
6150             this.scrollToElement(e);
6151         }
6152         
6153         
6154         var p =  this.parent();
6155    
6156         if (['tabs','pills'].indexOf(p.type)!==-1) {
6157             if (typeof(p.setActiveItem) !== 'undefined') {
6158                 p.setActiveItem(this);
6159             }
6160         }
6161         
6162         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6163         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6164             // remove the collapsed menu expand...
6165             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6166         }
6167     },
6168     
6169     isActive: function () {
6170         return this.active
6171     },
6172     setActive : function(state, fire, is_was_active)
6173     {
6174         if (this.active && !state && this.navId) {
6175             this.was_active = true;
6176             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6177             if (nv) {
6178                 nv.clearWasActive(this);
6179             }
6180             
6181         }
6182         this.active = state;
6183         
6184         if (!state ) {
6185             this.el.removeClass('active');
6186             this.navLink ? this.navLink.removeClass('active') : false;
6187         } else if (!this.el.hasClass('active')) {
6188             
6189             this.el.addClass('active');
6190             if (Roo.bootstrap.version == 4 && this.navLink ) {
6191                 this.navLink.addClass('active');
6192             }
6193             
6194         }
6195         if (fire) {
6196             this.fireEvent('changed', this, state);
6197         }
6198         
6199         // show a panel if it's registered and related..
6200         
6201         if (!this.navId || !this.tabId || !state || is_was_active) {
6202             return;
6203         }
6204         
6205         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6206         if (!tg) {
6207             return;
6208         }
6209         var pan = tg.getPanelByName(this.tabId);
6210         if (!pan) {
6211             return;
6212         }
6213         // if we can not flip to new panel - go back to old nav highlight..
6214         if (false == tg.showPanel(pan)) {
6215             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6216             if (nv) {
6217                 var onav = nv.getWasActive();
6218                 if (onav) {
6219                     onav.setActive(true, false, true);
6220                 }
6221             }
6222             
6223         }
6224         
6225         
6226         
6227     },
6228      // this should not be here...
6229     setDisabled : function(state)
6230     {
6231         this.disabled = state;
6232         if (!state ) {
6233             this.el.removeClass('disabled');
6234         } else if (!this.el.hasClass('disabled')) {
6235             this.el.addClass('disabled');
6236         }
6237         
6238     },
6239     
6240     /**
6241      * Fetch the element to display the tooltip on.
6242      * @return {Roo.Element} defaults to this.el
6243      */
6244     tooltipEl : function()
6245     {
6246         return this.el.select('' + this.tagtype + '', true).first();
6247     },
6248     
6249     scrollToElement : function(e)
6250     {
6251         var c = document.body;
6252         
6253         /*
6254          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6255          */
6256         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6257             c = document.documentElement;
6258         }
6259         
6260         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6261         
6262         if(!target){
6263             return;
6264         }
6265
6266         var o = target.calcOffsetsTo(c);
6267         
6268         var options = {
6269             target : target,
6270             value : o[1]
6271         };
6272         
6273         this.fireEvent('scrollto', this, options, e);
6274         
6275         Roo.get(c).scrollTo('top', options.value, true);
6276         
6277         return;
6278     }
6279 });
6280  
6281
6282  /*
6283  * - LGPL
6284  *
6285  * sidebar item
6286  *
6287  *  li
6288  *    <span> icon </span>
6289  *    <span> text </span>
6290  *    <span>badge </span>
6291  */
6292
6293 /**
6294  * @class Roo.bootstrap.NavSidebarItem
6295  * @extends Roo.bootstrap.NavItem
6296  * Bootstrap Navbar.NavSidebarItem class
6297  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6298  * {Boolean} open is the menu open
6299  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6300  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6301  * {String} buttonSize (sm|md|lg)the extra classes for the button
6302  * {Boolean} showArrow show arrow next to the text (default true)
6303  * @constructor
6304  * Create a new Navbar Button
6305  * @param {Object} config The config object
6306  */
6307 Roo.bootstrap.NavSidebarItem = function(config){
6308     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6309     this.addEvents({
6310         // raw events
6311         /**
6312          * @event click
6313          * The raw click event for the entire grid.
6314          * @param {Roo.EventObject} e
6315          */
6316         "click" : true,
6317          /**
6318             * @event changed
6319             * Fires when the active item active state changes
6320             * @param {Roo.bootstrap.NavSidebarItem} this
6321             * @param {boolean} state the new state
6322              
6323          */
6324         'changed': true
6325     });
6326    
6327 };
6328
6329 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6330     
6331     badgeWeight : 'default',
6332     
6333     open: false,
6334     
6335     buttonView : false,
6336     
6337     buttonWeight : 'default',
6338     
6339     buttonSize : 'md',
6340     
6341     showArrow : true,
6342     
6343     getAutoCreate : function(){
6344         
6345         
6346         var a = {
6347                 tag: 'a',
6348                 href : this.href || '#',
6349                 cls: '',
6350                 html : '',
6351                 cn : []
6352         };
6353         
6354         if(this.buttonView){
6355             a = {
6356                 tag: 'button',
6357                 href : this.href || '#',
6358                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6359                 html : this.html,
6360                 cn : []
6361             };
6362         }
6363         
6364         var cfg = {
6365             tag: 'li',
6366             cls: '',
6367             cn: [ a ]
6368         };
6369         
6370         if (this.active) {
6371             cfg.cls += ' active';
6372         }
6373         
6374         if (this.disabled) {
6375             cfg.cls += ' disabled';
6376         }
6377         if (this.open) {
6378             cfg.cls += ' open x-open';
6379         }
6380         // left icon..
6381         if (this.glyphicon || this.icon) {
6382             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6383             a.cn.push({ tag : 'i', cls : c }) ;
6384         }
6385         
6386         if(!this.buttonView){
6387             var span = {
6388                 tag: 'span',
6389                 html : this.html || ''
6390             };
6391
6392             a.cn.push(span);
6393             
6394         }
6395         
6396         if (this.badge !== '') {
6397             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6398         }
6399         
6400         if (this.menu) {
6401             
6402             if(this.showArrow){
6403                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6404             }
6405             
6406             a.cls += ' dropdown-toggle treeview' ;
6407         }
6408         
6409         return cfg;
6410     },
6411     
6412     initEvents : function()
6413     { 
6414         if (typeof (this.menu) != 'undefined') {
6415             this.menu.parentType = this.xtype;
6416             this.menu.triggerEl = this.el;
6417             this.menu = this.addxtype(Roo.apply({}, this.menu));
6418         }
6419         
6420         this.el.on('click', this.onClick, this);
6421         
6422         if(this.badge !== ''){
6423             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6424         }
6425         
6426     },
6427     
6428     onClick : function(e)
6429     {
6430         if(this.disabled){
6431             e.preventDefault();
6432             return;
6433         }
6434         
6435         if(this.preventDefault){
6436             e.preventDefault();
6437         }
6438         
6439         this.fireEvent('click', this, e);
6440     },
6441     
6442     disable : function()
6443     {
6444         this.setDisabled(true);
6445     },
6446     
6447     enable : function()
6448     {
6449         this.setDisabled(false);
6450     },
6451     
6452     setDisabled : function(state)
6453     {
6454         if(this.disabled == state){
6455             return;
6456         }
6457         
6458         this.disabled = state;
6459         
6460         if (state) {
6461             this.el.addClass('disabled');
6462             return;
6463         }
6464         
6465         this.el.removeClass('disabled');
6466         
6467         return;
6468     },
6469     
6470     setActive : function(state)
6471     {
6472         if(this.active == state){
6473             return;
6474         }
6475         
6476         this.active = state;
6477         
6478         if (state) {
6479             this.el.addClass('active');
6480             return;
6481         }
6482         
6483         this.el.removeClass('active');
6484         
6485         return;
6486     },
6487     
6488     isActive: function () 
6489     {
6490         return this.active;
6491     },
6492     
6493     setBadge : function(str)
6494     {
6495         if(!this.badgeEl){
6496             return;
6497         }
6498         
6499         this.badgeEl.dom.innerHTML = str;
6500     }
6501     
6502    
6503      
6504  
6505 });
6506  
6507
6508  /*
6509  * - LGPL
6510  *
6511  * row
6512  * 
6513  */
6514
6515 /**
6516  * @class Roo.bootstrap.Row
6517  * @extends Roo.bootstrap.Component
6518  * Bootstrap Row class (contains columns...)
6519  * 
6520  * @constructor
6521  * Create a new Row
6522  * @param {Object} config The config object
6523  */
6524
6525 Roo.bootstrap.Row = function(config){
6526     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6527 };
6528
6529 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6530     
6531     getAutoCreate : function(){
6532        return {
6533             cls: 'row clearfix'
6534        };
6535     }
6536     
6537     
6538 });
6539
6540  
6541
6542  /*
6543  * - LGPL
6544  *
6545  * pagination
6546  * 
6547  */
6548
6549 /**
6550  * @class Roo.bootstrap.Pagination
6551  * @extends Roo.bootstrap.Component
6552  * Bootstrap Pagination class
6553  * @cfg {String} size xs | sm | md | lg
6554  * @cfg {Boolean} inverse false | true
6555  * 
6556  * @constructor
6557  * Create a new Pagination
6558  * @param {Object} config The config object
6559  */
6560
6561 Roo.bootstrap.Pagination = function(config){
6562     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6563 };
6564
6565 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6566     
6567     cls: false,
6568     size: false,
6569     inverse: false,
6570     
6571     getAutoCreate : function(){
6572         var cfg = {
6573             tag: 'ul',
6574                 cls: 'pagination'
6575         };
6576         if (this.inverse) {
6577             cfg.cls += ' inverse';
6578         }
6579         if (this.html) {
6580             cfg.html=this.html;
6581         }
6582         if (this.cls) {
6583             cfg.cls += " " + this.cls;
6584         }
6585         return cfg;
6586     }
6587    
6588 });
6589
6590  
6591
6592  /*
6593  * - LGPL
6594  *
6595  * Pagination item
6596  * 
6597  */
6598
6599
6600 /**
6601  * @class Roo.bootstrap.PaginationItem
6602  * @extends Roo.bootstrap.Component
6603  * Bootstrap PaginationItem class
6604  * @cfg {String} html text
6605  * @cfg {String} href the link
6606  * @cfg {Boolean} preventDefault (true | false) default true
6607  * @cfg {Boolean} active (true | false) default false
6608  * @cfg {Boolean} disabled default false
6609  * 
6610  * 
6611  * @constructor
6612  * Create a new PaginationItem
6613  * @param {Object} config The config object
6614  */
6615
6616
6617 Roo.bootstrap.PaginationItem = function(config){
6618     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6619     this.addEvents({
6620         // raw events
6621         /**
6622          * @event click
6623          * The raw click event for the entire grid.
6624          * @param {Roo.EventObject} e
6625          */
6626         "click" : true
6627     });
6628 };
6629
6630 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6631     
6632     href : false,
6633     html : false,
6634     preventDefault: true,
6635     active : false,
6636     cls : false,
6637     disabled: false,
6638     
6639     getAutoCreate : function(){
6640         var cfg= {
6641             tag: 'li',
6642             cn: [
6643                 {
6644                     tag : 'a',
6645                     href : this.href ? this.href : '#',
6646                     html : this.html ? this.html : ''
6647                 }
6648             ]
6649         };
6650         
6651         if(this.cls){
6652             cfg.cls = this.cls;
6653         }
6654         
6655         if(this.disabled){
6656             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6657         }
6658         
6659         if(this.active){
6660             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6661         }
6662         
6663         return cfg;
6664     },
6665     
6666     initEvents: function() {
6667         
6668         this.el.on('click', this.onClick, this);
6669         
6670     },
6671     onClick : function(e)
6672     {
6673         Roo.log('PaginationItem on click ');
6674         if(this.preventDefault){
6675             e.preventDefault();
6676         }
6677         
6678         if(this.disabled){
6679             return;
6680         }
6681         
6682         this.fireEvent('click', this, e);
6683     }
6684    
6685 });
6686
6687  
6688
6689  /*
6690  * - LGPL
6691  *
6692  * slider
6693  * 
6694  */
6695
6696
6697 /**
6698  * @class Roo.bootstrap.Slider
6699  * @extends Roo.bootstrap.Component
6700  * Bootstrap Slider class
6701  *    
6702  * @constructor
6703  * Create a new Slider
6704  * @param {Object} config The config object
6705  */
6706
6707 Roo.bootstrap.Slider = function(config){
6708     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6709 };
6710
6711 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6712     
6713     getAutoCreate : function(){
6714         
6715         var cfg = {
6716             tag: 'div',
6717             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6718             cn: [
6719                 {
6720                     tag: 'a',
6721                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6722                 }
6723             ]
6724         };
6725         
6726         return cfg;
6727     }
6728    
6729 });
6730
6731  /*
6732  * Based on:
6733  * Ext JS Library 1.1.1
6734  * Copyright(c) 2006-2007, Ext JS, LLC.
6735  *
6736  * Originally Released Under LGPL - original licence link has changed is not relivant.
6737  *
6738  * Fork - LGPL
6739  * <script type="text/javascript">
6740  */
6741  
6742
6743 /**
6744  * @class Roo.grid.ColumnModel
6745  * @extends Roo.util.Observable
6746  * This is the default implementation of a ColumnModel used by the Grid. It defines
6747  * the columns in the grid.
6748  * <br>Usage:<br>
6749  <pre><code>
6750  var colModel = new Roo.grid.ColumnModel([
6751         {header: "Ticker", width: 60, sortable: true, locked: true},
6752         {header: "Company Name", width: 150, sortable: true},
6753         {header: "Market Cap.", width: 100, sortable: true},
6754         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6755         {header: "Employees", width: 100, sortable: true, resizable: false}
6756  ]);
6757  </code></pre>
6758  * <p>
6759  
6760  * The config options listed for this class are options which may appear in each
6761  * individual column definition.
6762  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6763  * @constructor
6764  * @param {Object} config An Array of column config objects. See this class's
6765  * config objects for details.
6766 */
6767 Roo.grid.ColumnModel = function(config){
6768         /**
6769      * The config passed into the constructor
6770      */
6771     this.config = config;
6772     this.lookup = {};
6773
6774     // if no id, create one
6775     // if the column does not have a dataIndex mapping,
6776     // map it to the order it is in the config
6777     for(var i = 0, len = config.length; i < len; i++){
6778         var c = config[i];
6779         if(typeof c.dataIndex == "undefined"){
6780             c.dataIndex = i;
6781         }
6782         if(typeof c.renderer == "string"){
6783             c.renderer = Roo.util.Format[c.renderer];
6784         }
6785         if(typeof c.id == "undefined"){
6786             c.id = Roo.id();
6787         }
6788         if(c.editor && c.editor.xtype){
6789             c.editor  = Roo.factory(c.editor, Roo.grid);
6790         }
6791         if(c.editor && c.editor.isFormField){
6792             c.editor = new Roo.grid.GridEditor(c.editor);
6793         }
6794         this.lookup[c.id] = c;
6795     }
6796
6797     /**
6798      * The width of columns which have no width specified (defaults to 100)
6799      * @type Number
6800      */
6801     this.defaultWidth = 100;
6802
6803     /**
6804      * Default sortable of columns which have no sortable specified (defaults to false)
6805      * @type Boolean
6806      */
6807     this.defaultSortable = false;
6808
6809     this.addEvents({
6810         /**
6811              * @event widthchange
6812              * Fires when the width of a column changes.
6813              * @param {ColumnModel} this
6814              * @param {Number} columnIndex The column index
6815              * @param {Number} newWidth The new width
6816              */
6817             "widthchange": true,
6818         /**
6819              * @event headerchange
6820              * Fires when the text of a header changes.
6821              * @param {ColumnModel} this
6822              * @param {Number} columnIndex The column index
6823              * @param {Number} newText The new header text
6824              */
6825             "headerchange": true,
6826         /**
6827              * @event hiddenchange
6828              * Fires when a column is hidden or "unhidden".
6829              * @param {ColumnModel} this
6830              * @param {Number} columnIndex The column index
6831              * @param {Boolean} hidden true if hidden, false otherwise
6832              */
6833             "hiddenchange": true,
6834             /**
6835          * @event columnmoved
6836          * Fires when a column is moved.
6837          * @param {ColumnModel} this
6838          * @param {Number} oldIndex
6839          * @param {Number} newIndex
6840          */
6841         "columnmoved" : true,
6842         /**
6843          * @event columlockchange
6844          * Fires when a column's locked state is changed
6845          * @param {ColumnModel} this
6846          * @param {Number} colIndex
6847          * @param {Boolean} locked true if locked
6848          */
6849         "columnlockchange" : true
6850     });
6851     Roo.grid.ColumnModel.superclass.constructor.call(this);
6852 };
6853 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6854     /**
6855      * @cfg {String} header The header text to display in the Grid view.
6856      */
6857     /**
6858      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6859      * {@link Roo.data.Record} definition from which to draw the column's value. If not
6860      * specified, the column's index is used as an index into the Record's data Array.
6861      */
6862     /**
6863      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6864      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6865      */
6866     /**
6867      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6868      * Defaults to the value of the {@link #defaultSortable} property.
6869      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6870      */
6871     /**
6872      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
6873      */
6874     /**
6875      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
6876      */
6877     /**
6878      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6879      */
6880     /**
6881      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6882      */
6883     /**
6884      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6885      * given the cell's data value. See {@link #setRenderer}. If not specified, the
6886      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6887      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6888      */
6889        /**
6890      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
6891      */
6892     /**
6893      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
6894      */
6895     /**
6896      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
6897      */
6898     /**
6899      * @cfg {String} cursor (Optional)
6900      */
6901     /**
6902      * @cfg {String} tooltip (Optional)
6903      */
6904     /**
6905      * @cfg {Number} xs (Optional)
6906      */
6907     /**
6908      * @cfg {Number} sm (Optional)
6909      */
6910     /**
6911      * @cfg {Number} md (Optional)
6912      */
6913     /**
6914      * @cfg {Number} lg (Optional)
6915      */
6916     /**
6917      * Returns the id of the column at the specified index.
6918      * @param {Number} index The column index
6919      * @return {String} the id
6920      */
6921     getColumnId : function(index){
6922         return this.config[index].id;
6923     },
6924
6925     /**
6926      * Returns the column for a specified id.
6927      * @param {String} id The column id
6928      * @return {Object} the column
6929      */
6930     getColumnById : function(id){
6931         return this.lookup[id];
6932     },
6933
6934     
6935     /**
6936      * Returns the column for a specified dataIndex.
6937      * @param {String} dataIndex The column dataIndex
6938      * @return {Object|Boolean} the column or false if not found
6939      */
6940     getColumnByDataIndex: function(dataIndex){
6941         var index = this.findColumnIndex(dataIndex);
6942         return index > -1 ? this.config[index] : false;
6943     },
6944     
6945     /**
6946      * Returns the index for a specified column id.
6947      * @param {String} id The column id
6948      * @return {Number} the index, or -1 if not found
6949      */
6950     getIndexById : function(id){
6951         for(var i = 0, len = this.config.length; i < len; i++){
6952             if(this.config[i].id == id){
6953                 return i;
6954             }
6955         }
6956         return -1;
6957     },
6958     
6959     /**
6960      * Returns the index for a specified column dataIndex.
6961      * @param {String} dataIndex The column dataIndex
6962      * @return {Number} the index, or -1 if not found
6963      */
6964     
6965     findColumnIndex : function(dataIndex){
6966         for(var i = 0, len = this.config.length; i < len; i++){
6967             if(this.config[i].dataIndex == dataIndex){
6968                 return i;
6969             }
6970         }
6971         return -1;
6972     },
6973     
6974     
6975     moveColumn : function(oldIndex, newIndex){
6976         var c = this.config[oldIndex];
6977         this.config.splice(oldIndex, 1);
6978         this.config.splice(newIndex, 0, c);
6979         this.dataMap = null;
6980         this.fireEvent("columnmoved", this, oldIndex, newIndex);
6981     },
6982
6983     isLocked : function(colIndex){
6984         return this.config[colIndex].locked === true;
6985     },
6986
6987     setLocked : function(colIndex, value, suppressEvent){
6988         if(this.isLocked(colIndex) == value){
6989             return;
6990         }
6991         this.config[colIndex].locked = value;
6992         if(!suppressEvent){
6993             this.fireEvent("columnlockchange", this, colIndex, value);
6994         }
6995     },
6996
6997     getTotalLockedWidth : function(){
6998         var totalWidth = 0;
6999         for(var i = 0; i < this.config.length; i++){
7000             if(this.isLocked(i) && !this.isHidden(i)){
7001                 this.totalWidth += this.getColumnWidth(i);
7002             }
7003         }
7004         return totalWidth;
7005     },
7006
7007     getLockedCount : function(){
7008         for(var i = 0, len = this.config.length; i < len; i++){
7009             if(!this.isLocked(i)){
7010                 return i;
7011             }
7012         }
7013         
7014         return this.config.length;
7015     },
7016
7017     /**
7018      * Returns the number of columns.
7019      * @return {Number}
7020      */
7021     getColumnCount : function(visibleOnly){
7022         if(visibleOnly === true){
7023             var c = 0;
7024             for(var i = 0, len = this.config.length; i < len; i++){
7025                 if(!this.isHidden(i)){
7026                     c++;
7027                 }
7028             }
7029             return c;
7030         }
7031         return this.config.length;
7032     },
7033
7034     /**
7035      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7036      * @param {Function} fn
7037      * @param {Object} scope (optional)
7038      * @return {Array} result
7039      */
7040     getColumnsBy : function(fn, scope){
7041         var r = [];
7042         for(var i = 0, len = this.config.length; i < len; i++){
7043             var c = this.config[i];
7044             if(fn.call(scope||this, c, i) === true){
7045                 r[r.length] = c;
7046             }
7047         }
7048         return r;
7049     },
7050
7051     /**
7052      * Returns true if the specified column is sortable.
7053      * @param {Number} col The column index
7054      * @return {Boolean}
7055      */
7056     isSortable : function(col){
7057         if(typeof this.config[col].sortable == "undefined"){
7058             return this.defaultSortable;
7059         }
7060         return this.config[col].sortable;
7061     },
7062
7063     /**
7064      * Returns the rendering (formatting) function defined for the column.
7065      * @param {Number} col The column index.
7066      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7067      */
7068     getRenderer : function(col){
7069         if(!this.config[col].renderer){
7070             return Roo.grid.ColumnModel.defaultRenderer;
7071         }
7072         return this.config[col].renderer;
7073     },
7074
7075     /**
7076      * Sets the rendering (formatting) function for a column.
7077      * @param {Number} col The column index
7078      * @param {Function} fn The function to use to process the cell's raw data
7079      * to return HTML markup for the grid view. The render function is called with
7080      * the following parameters:<ul>
7081      * <li>Data value.</li>
7082      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7083      * <li>css A CSS style string to apply to the table cell.</li>
7084      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7085      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7086      * <li>Row index</li>
7087      * <li>Column index</li>
7088      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7089      */
7090     setRenderer : function(col, fn){
7091         this.config[col].renderer = fn;
7092     },
7093
7094     /**
7095      * Returns the width for the specified column.
7096      * @param {Number} col The column index
7097      * @return {Number}
7098      */
7099     getColumnWidth : function(col){
7100         return this.config[col].width * 1 || this.defaultWidth;
7101     },
7102
7103     /**
7104      * Sets the width for a column.
7105      * @param {Number} col The column index
7106      * @param {Number} width The new width
7107      */
7108     setColumnWidth : function(col, width, suppressEvent){
7109         this.config[col].width = width;
7110         this.totalWidth = null;
7111         if(!suppressEvent){
7112              this.fireEvent("widthchange", this, col, width);
7113         }
7114     },
7115
7116     /**
7117      * Returns the total width of all columns.
7118      * @param {Boolean} includeHidden True to include hidden column widths
7119      * @return {Number}
7120      */
7121     getTotalWidth : function(includeHidden){
7122         if(!this.totalWidth){
7123             this.totalWidth = 0;
7124             for(var i = 0, len = this.config.length; i < len; i++){
7125                 if(includeHidden || !this.isHidden(i)){
7126                     this.totalWidth += this.getColumnWidth(i);
7127                 }
7128             }
7129         }
7130         return this.totalWidth;
7131     },
7132
7133     /**
7134      * Returns the header for the specified column.
7135      * @param {Number} col The column index
7136      * @return {String}
7137      */
7138     getColumnHeader : function(col){
7139         return this.config[col].header;
7140     },
7141
7142     /**
7143      * Sets the header for a column.
7144      * @param {Number} col The column index
7145      * @param {String} header The new header
7146      */
7147     setColumnHeader : function(col, header){
7148         this.config[col].header = header;
7149         this.fireEvent("headerchange", this, col, header);
7150     },
7151
7152     /**
7153      * Returns the tooltip for the specified column.
7154      * @param {Number} col The column index
7155      * @return {String}
7156      */
7157     getColumnTooltip : function(col){
7158             return this.config[col].tooltip;
7159     },
7160     /**
7161      * Sets the tooltip for a column.
7162      * @param {Number} col The column index
7163      * @param {String} tooltip The new tooltip
7164      */
7165     setColumnTooltip : function(col, tooltip){
7166             this.config[col].tooltip = tooltip;
7167     },
7168
7169     /**
7170      * Returns the dataIndex for the specified column.
7171      * @param {Number} col The column index
7172      * @return {Number}
7173      */
7174     getDataIndex : function(col){
7175         return this.config[col].dataIndex;
7176     },
7177
7178     /**
7179      * Sets the dataIndex for a column.
7180      * @param {Number} col The column index
7181      * @param {Number} dataIndex The new dataIndex
7182      */
7183     setDataIndex : function(col, dataIndex){
7184         this.config[col].dataIndex = dataIndex;
7185     },
7186
7187     
7188     
7189     /**
7190      * Returns true if the cell is editable.
7191      * @param {Number} colIndex The column index
7192      * @param {Number} rowIndex The row index - this is nto actually used..?
7193      * @return {Boolean}
7194      */
7195     isCellEditable : function(colIndex, rowIndex){
7196         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7197     },
7198
7199     /**
7200      * Returns the editor defined for the cell/column.
7201      * return false or null to disable editing.
7202      * @param {Number} colIndex The column index
7203      * @param {Number} rowIndex The row index
7204      * @return {Object}
7205      */
7206     getCellEditor : function(colIndex, rowIndex){
7207         return this.config[colIndex].editor;
7208     },
7209
7210     /**
7211      * Sets if a column is editable.
7212      * @param {Number} col The column index
7213      * @param {Boolean} editable True if the column is editable
7214      */
7215     setEditable : function(col, editable){
7216         this.config[col].editable = editable;
7217     },
7218
7219
7220     /**
7221      * Returns true if the column is hidden.
7222      * @param {Number} colIndex The column index
7223      * @return {Boolean}
7224      */
7225     isHidden : function(colIndex){
7226         return this.config[colIndex].hidden;
7227     },
7228
7229
7230     /**
7231      * Returns true if the column width cannot be changed
7232      */
7233     isFixed : function(colIndex){
7234         return this.config[colIndex].fixed;
7235     },
7236
7237     /**
7238      * Returns true if the column can be resized
7239      * @return {Boolean}
7240      */
7241     isResizable : function(colIndex){
7242         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7243     },
7244     /**
7245      * Sets if a column is hidden.
7246      * @param {Number} colIndex The column index
7247      * @param {Boolean} hidden True if the column is hidden
7248      */
7249     setHidden : function(colIndex, hidden){
7250         this.config[colIndex].hidden = hidden;
7251         this.totalWidth = null;
7252         this.fireEvent("hiddenchange", this, colIndex, hidden);
7253     },
7254
7255     /**
7256      * Sets the editor for a column.
7257      * @param {Number} col The column index
7258      * @param {Object} editor The editor object
7259      */
7260     setEditor : function(col, editor){
7261         this.config[col].editor = editor;
7262     }
7263 });
7264
7265 Roo.grid.ColumnModel.defaultRenderer = function(value)
7266 {
7267     if(typeof value == "object") {
7268         return value;
7269     }
7270         if(typeof value == "string" && value.length < 1){
7271             return "&#160;";
7272         }
7273     
7274         return String.format("{0}", value);
7275 };
7276
7277 // Alias for backwards compatibility
7278 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7279 /*
7280  * Based on:
7281  * Ext JS Library 1.1.1
7282  * Copyright(c) 2006-2007, Ext JS, LLC.
7283  *
7284  * Originally Released Under LGPL - original licence link has changed is not relivant.
7285  *
7286  * Fork - LGPL
7287  * <script type="text/javascript">
7288  */
7289  
7290 /**
7291  * @class Roo.LoadMask
7292  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7293  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7294  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7295  * element's UpdateManager load indicator and will be destroyed after the initial load.
7296  * @constructor
7297  * Create a new LoadMask
7298  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7299  * @param {Object} config The config object
7300  */
7301 Roo.LoadMask = function(el, config){
7302     this.el = Roo.get(el);
7303     Roo.apply(this, config);
7304     if(this.store){
7305         this.store.on('beforeload', this.onBeforeLoad, this);
7306         this.store.on('load', this.onLoad, this);
7307         this.store.on('loadexception', this.onLoadException, this);
7308         this.removeMask = false;
7309     }else{
7310         var um = this.el.getUpdateManager();
7311         um.showLoadIndicator = false; // disable the default indicator
7312         um.on('beforeupdate', this.onBeforeLoad, this);
7313         um.on('update', this.onLoad, this);
7314         um.on('failure', this.onLoad, this);
7315         this.removeMask = true;
7316     }
7317 };
7318
7319 Roo.LoadMask.prototype = {
7320     /**
7321      * @cfg {Boolean} removeMask
7322      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7323      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7324      */
7325     /**
7326      * @cfg {String} msg
7327      * The text to display in a centered loading message box (defaults to 'Loading...')
7328      */
7329     msg : 'Loading...',
7330     /**
7331      * @cfg {String} msgCls
7332      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7333      */
7334     msgCls : 'x-mask-loading',
7335
7336     /**
7337      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7338      * @type Boolean
7339      */
7340     disabled: false,
7341
7342     /**
7343      * Disables the mask to prevent it from being displayed
7344      */
7345     disable : function(){
7346        this.disabled = true;
7347     },
7348
7349     /**
7350      * Enables the mask so that it can be displayed
7351      */
7352     enable : function(){
7353         this.disabled = false;
7354     },
7355     
7356     onLoadException : function()
7357     {
7358         Roo.log(arguments);
7359         
7360         if (typeof(arguments[3]) != 'undefined') {
7361             Roo.MessageBox.alert("Error loading",arguments[3]);
7362         } 
7363         /*
7364         try {
7365             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7366                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7367             }   
7368         } catch(e) {
7369             
7370         }
7371         */
7372     
7373         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7374     },
7375     // private
7376     onLoad : function()
7377     {
7378         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7379     },
7380
7381     // private
7382     onBeforeLoad : function(){
7383         if(!this.disabled){
7384             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7385         }
7386     },
7387
7388     // private
7389     destroy : function(){
7390         if(this.store){
7391             this.store.un('beforeload', this.onBeforeLoad, this);
7392             this.store.un('load', this.onLoad, this);
7393             this.store.un('loadexception', this.onLoadException, this);
7394         }else{
7395             var um = this.el.getUpdateManager();
7396             um.un('beforeupdate', this.onBeforeLoad, this);
7397             um.un('update', this.onLoad, this);
7398             um.un('failure', this.onLoad, this);
7399         }
7400     }
7401 };/*
7402  * - LGPL
7403  *
7404  * table
7405  * 
7406  */
7407
7408 /**
7409  * @class Roo.bootstrap.Table
7410  * @extends Roo.bootstrap.Component
7411  * Bootstrap Table class
7412  * @cfg {String} cls table class
7413  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7414  * @cfg {String} bgcolor Specifies the background color for a table
7415  * @cfg {Number} border Specifies whether the table cells should have borders or not
7416  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7417  * @cfg {Number} cellspacing Specifies the space between cells
7418  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7419  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7420  * @cfg {String} sortable Specifies that the table should be sortable
7421  * @cfg {String} summary Specifies a summary of the content of a table
7422  * @cfg {Number} width Specifies the width of a table
7423  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7424  * 
7425  * @cfg {boolean} striped Should the rows be alternative striped
7426  * @cfg {boolean} bordered Add borders to the table
7427  * @cfg {boolean} hover Add hover highlighting
7428  * @cfg {boolean} condensed Format condensed
7429  * @cfg {boolean} responsive Format condensed
7430  * @cfg {Boolean} loadMask (true|false) default false
7431  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7432  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7433  * @cfg {Boolean} rowSelection (true|false) default false
7434  * @cfg {Boolean} cellSelection (true|false) default false
7435  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7436  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7437  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7438  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7439  
7440  * 
7441  * @constructor
7442  * Create a new Table
7443  * @param {Object} config The config object
7444  */
7445
7446 Roo.bootstrap.Table = function(config){
7447     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7448     
7449   
7450     
7451     // BC...
7452     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7453     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7454     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7455     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7456     
7457     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7458     if (this.sm) {
7459         this.sm.grid = this;
7460         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7461         this.sm = this.selModel;
7462         this.sm.xmodule = this.xmodule || false;
7463     }
7464     
7465     if (this.cm && typeof(this.cm.config) == 'undefined') {
7466         this.colModel = new Roo.grid.ColumnModel(this.cm);
7467         this.cm = this.colModel;
7468         this.cm.xmodule = this.xmodule || false;
7469     }
7470     if (this.store) {
7471         this.store= Roo.factory(this.store, Roo.data);
7472         this.ds = this.store;
7473         this.ds.xmodule = this.xmodule || false;
7474          
7475     }
7476     if (this.footer && this.store) {
7477         this.footer.dataSource = this.ds;
7478         this.footer = Roo.factory(this.footer);
7479     }
7480     
7481     /** @private */
7482     this.addEvents({
7483         /**
7484          * @event cellclick
7485          * Fires when a cell is clicked
7486          * @param {Roo.bootstrap.Table} this
7487          * @param {Roo.Element} el
7488          * @param {Number} rowIndex
7489          * @param {Number} columnIndex
7490          * @param {Roo.EventObject} e
7491          */
7492         "cellclick" : true,
7493         /**
7494          * @event celldblclick
7495          * Fires when a cell is double clicked
7496          * @param {Roo.bootstrap.Table} this
7497          * @param {Roo.Element} el
7498          * @param {Number} rowIndex
7499          * @param {Number} columnIndex
7500          * @param {Roo.EventObject} e
7501          */
7502         "celldblclick" : true,
7503         /**
7504          * @event rowclick
7505          * Fires when a row is clicked
7506          * @param {Roo.bootstrap.Table} this
7507          * @param {Roo.Element} el
7508          * @param {Number} rowIndex
7509          * @param {Roo.EventObject} e
7510          */
7511         "rowclick" : true,
7512         /**
7513          * @event rowdblclick
7514          * Fires when a row is double clicked
7515          * @param {Roo.bootstrap.Table} this
7516          * @param {Roo.Element} el
7517          * @param {Number} rowIndex
7518          * @param {Roo.EventObject} e
7519          */
7520         "rowdblclick" : true,
7521         /**
7522          * @event mouseover
7523          * Fires when a mouseover occur
7524          * @param {Roo.bootstrap.Table} this
7525          * @param {Roo.Element} el
7526          * @param {Number} rowIndex
7527          * @param {Number} columnIndex
7528          * @param {Roo.EventObject} e
7529          */
7530         "mouseover" : true,
7531         /**
7532          * @event mouseout
7533          * Fires when a mouseout occur
7534          * @param {Roo.bootstrap.Table} this
7535          * @param {Roo.Element} el
7536          * @param {Number} rowIndex
7537          * @param {Number} columnIndex
7538          * @param {Roo.EventObject} e
7539          */
7540         "mouseout" : true,
7541         /**
7542          * @event rowclass
7543          * Fires when a row is rendered, so you can change add a style to it.
7544          * @param {Roo.bootstrap.Table} this
7545          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7546          */
7547         'rowclass' : true,
7548           /**
7549          * @event rowsrendered
7550          * Fires when all the  rows have been rendered
7551          * @param {Roo.bootstrap.Table} this
7552          */
7553         'rowsrendered' : true,
7554         /**
7555          * @event contextmenu
7556          * The raw contextmenu event for the entire grid.
7557          * @param {Roo.EventObject} e
7558          */
7559         "contextmenu" : true,
7560         /**
7561          * @event rowcontextmenu
7562          * Fires when a row is right clicked
7563          * @param {Roo.bootstrap.Table} this
7564          * @param {Number} rowIndex
7565          * @param {Roo.EventObject} e
7566          */
7567         "rowcontextmenu" : true,
7568         /**
7569          * @event cellcontextmenu
7570          * Fires when a cell is right clicked
7571          * @param {Roo.bootstrap.Table} this
7572          * @param {Number} rowIndex
7573          * @param {Number} cellIndex
7574          * @param {Roo.EventObject} e
7575          */
7576          "cellcontextmenu" : true,
7577          /**
7578          * @event headercontextmenu
7579          * Fires when a header is right clicked
7580          * @param {Roo.bootstrap.Table} this
7581          * @param {Number} columnIndex
7582          * @param {Roo.EventObject} e
7583          */
7584         "headercontextmenu" : true
7585     });
7586 };
7587
7588 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7589     
7590     cls: false,
7591     align: false,
7592     bgcolor: false,
7593     border: false,
7594     cellpadding: false,
7595     cellspacing: false,
7596     frame: false,
7597     rules: false,
7598     sortable: false,
7599     summary: false,
7600     width: false,
7601     striped : false,
7602     scrollBody : false,
7603     bordered: false,
7604     hover:  false,
7605     condensed : false,
7606     responsive : false,
7607     sm : false,
7608     cm : false,
7609     store : false,
7610     loadMask : false,
7611     footerShow : true,
7612     headerShow : true,
7613   
7614     rowSelection : false,
7615     cellSelection : false,
7616     layout : false,
7617     
7618     // Roo.Element - the tbody
7619     mainBody: false,
7620     // Roo.Element - thead element
7621     mainHead: false,
7622     
7623     container: false, // used by gridpanel...
7624     
7625     lazyLoad : false,
7626     
7627     CSS : Roo.util.CSS,
7628     
7629     auto_hide_footer : false,
7630     
7631     getAutoCreate : function()
7632     {
7633         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7634         
7635         cfg = {
7636             tag: 'table',
7637             cls : 'table',
7638             cn : []
7639         };
7640         if (this.scrollBody) {
7641             cfg.cls += ' table-body-fixed';
7642         }    
7643         if (this.striped) {
7644             cfg.cls += ' table-striped';
7645         }
7646         
7647         if (this.hover) {
7648             cfg.cls += ' table-hover';
7649         }
7650         if (this.bordered) {
7651             cfg.cls += ' table-bordered';
7652         }
7653         if (this.condensed) {
7654             cfg.cls += ' table-condensed';
7655         }
7656         if (this.responsive) {
7657             cfg.cls += ' table-responsive';
7658         }
7659         
7660         if (this.cls) {
7661             cfg.cls+=  ' ' +this.cls;
7662         }
7663         
7664         // this lot should be simplifed...
7665         var _t = this;
7666         var cp = [
7667             'align',
7668             'bgcolor',
7669             'border',
7670             'cellpadding',
7671             'cellspacing',
7672             'frame',
7673             'rules',
7674             'sortable',
7675             'summary',
7676             'width'
7677         ].forEach(function(k) {
7678             if (_t[k]) {
7679                 cfg[k] = _t[k];
7680             }
7681         });
7682         
7683         
7684         if (this.layout) {
7685             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7686         }
7687         
7688         if(this.store || this.cm){
7689             if(this.headerShow){
7690                 cfg.cn.push(this.renderHeader());
7691             }
7692             
7693             cfg.cn.push(this.renderBody());
7694             
7695             if(this.footerShow){
7696                 cfg.cn.push(this.renderFooter());
7697             }
7698             // where does this come from?
7699             //cfg.cls+=  ' TableGrid';
7700         }
7701         
7702         return { cn : [ cfg ] };
7703     },
7704     
7705     initEvents : function()
7706     {   
7707         if(!this.store || !this.cm){
7708             return;
7709         }
7710         if (this.selModel) {
7711             this.selModel.initEvents();
7712         }
7713         
7714         
7715         //Roo.log('initEvents with ds!!!!');
7716         
7717         this.mainBody = this.el.select('tbody', true).first();
7718         this.mainHead = this.el.select('thead', true).first();
7719         this.mainFoot = this.el.select('tfoot', true).first();
7720         
7721         
7722         
7723         var _this = this;
7724         
7725         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7726             e.on('click', _this.sort, _this);
7727         });
7728         
7729         this.mainBody.on("click", this.onClick, this);
7730         this.mainBody.on("dblclick", this.onDblClick, this);
7731         
7732         // why is this done????? = it breaks dialogs??
7733         //this.parent().el.setStyle('position', 'relative');
7734         
7735         
7736         if (this.footer) {
7737             this.footer.parentId = this.id;
7738             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7739             
7740             if(this.lazyLoad){
7741                 this.el.select('tfoot tr td').first().addClass('hide');
7742             }
7743         } 
7744         
7745         if(this.loadMask) {
7746             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7747         }
7748         
7749         this.store.on('load', this.onLoad, this);
7750         this.store.on('beforeload', this.onBeforeLoad, this);
7751         this.store.on('update', this.onUpdate, this);
7752         this.store.on('add', this.onAdd, this);
7753         this.store.on("clear", this.clear, this);
7754         
7755         this.el.on("contextmenu", this.onContextMenu, this);
7756         
7757         this.mainBody.on('scroll', this.onBodyScroll, this);
7758         
7759         this.cm.on("headerchange", this.onHeaderChange, this);
7760         
7761         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7762         
7763     },
7764     
7765     onContextMenu : function(e, t)
7766     {
7767         this.processEvent("contextmenu", e);
7768     },
7769     
7770     processEvent : function(name, e)
7771     {
7772         if (name != 'touchstart' ) {
7773             this.fireEvent(name, e);    
7774         }
7775         
7776         var t = e.getTarget();
7777         
7778         var cell = Roo.get(t);
7779         
7780         if(!cell){
7781             return;
7782         }
7783         
7784         if(cell.findParent('tfoot', false, true)){
7785             return;
7786         }
7787         
7788         if(cell.findParent('thead', false, true)){
7789             
7790             if(e.getTarget().nodeName.toLowerCase() != 'th'){
7791                 cell = Roo.get(t).findParent('th', false, true);
7792                 if (!cell) {
7793                     Roo.log("failed to find th in thead?");
7794                     Roo.log(e.getTarget());
7795                     return;
7796                 }
7797             }
7798             
7799             var cellIndex = cell.dom.cellIndex;
7800             
7801             var ename = name == 'touchstart' ? 'click' : name;
7802             this.fireEvent("header" + ename, this, cellIndex, e);
7803             
7804             return;
7805         }
7806         
7807         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7808             cell = Roo.get(t).findParent('td', false, true);
7809             if (!cell) {
7810                 Roo.log("failed to find th in tbody?");
7811                 Roo.log(e.getTarget());
7812                 return;
7813             }
7814         }
7815         
7816         var row = cell.findParent('tr', false, true);
7817         var cellIndex = cell.dom.cellIndex;
7818         var rowIndex = row.dom.rowIndex - 1;
7819         
7820         if(row !== false){
7821             
7822             this.fireEvent("row" + name, this, rowIndex, e);
7823             
7824             if(cell !== false){
7825             
7826                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7827             }
7828         }
7829         
7830     },
7831     
7832     onMouseover : function(e, el)
7833     {
7834         var cell = Roo.get(el);
7835         
7836         if(!cell){
7837             return;
7838         }
7839         
7840         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7841             cell = cell.findParent('td', false, true);
7842         }
7843         
7844         var row = cell.findParent('tr', false, true);
7845         var cellIndex = cell.dom.cellIndex;
7846         var rowIndex = row.dom.rowIndex - 1; // start from 0
7847         
7848         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7849         
7850     },
7851     
7852     onMouseout : function(e, el)
7853     {
7854         var cell = Roo.get(el);
7855         
7856         if(!cell){
7857             return;
7858         }
7859         
7860         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7861             cell = cell.findParent('td', false, true);
7862         }
7863         
7864         var row = cell.findParent('tr', false, true);
7865         var cellIndex = cell.dom.cellIndex;
7866         var rowIndex = row.dom.rowIndex - 1; // start from 0
7867         
7868         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7869         
7870     },
7871     
7872     onClick : function(e, el)
7873     {
7874         var cell = Roo.get(el);
7875         
7876         if(!cell || (!this.cellSelection && !this.rowSelection)){
7877             return;
7878         }
7879         
7880         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7881             cell = cell.findParent('td', false, true);
7882         }
7883         
7884         if(!cell || typeof(cell) == 'undefined'){
7885             return;
7886         }
7887         
7888         var row = cell.findParent('tr', false, true);
7889         
7890         if(!row || typeof(row) == 'undefined'){
7891             return;
7892         }
7893         
7894         var cellIndex = cell.dom.cellIndex;
7895         var rowIndex = this.getRowIndex(row);
7896         
7897         // why??? - should these not be based on SelectionModel?
7898         if(this.cellSelection){
7899             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7900         }
7901         
7902         if(this.rowSelection){
7903             this.fireEvent('rowclick', this, row, rowIndex, e);
7904         }
7905         
7906         
7907     },
7908         
7909     onDblClick : function(e,el)
7910     {
7911         var cell = Roo.get(el);
7912         
7913         if(!cell || (!this.cellSelection && !this.rowSelection)){
7914             return;
7915         }
7916         
7917         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7918             cell = cell.findParent('td', false, true);
7919         }
7920         
7921         if(!cell || typeof(cell) == 'undefined'){
7922             return;
7923         }
7924         
7925         var row = cell.findParent('tr', false, true);
7926         
7927         if(!row || typeof(row) == 'undefined'){
7928             return;
7929         }
7930         
7931         var cellIndex = cell.dom.cellIndex;
7932         var rowIndex = this.getRowIndex(row);
7933         
7934         if(this.cellSelection){
7935             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7936         }
7937         
7938         if(this.rowSelection){
7939             this.fireEvent('rowdblclick', this, row, rowIndex, e);
7940         }
7941     },
7942     
7943     sort : function(e,el)
7944     {
7945         var col = Roo.get(el);
7946         
7947         if(!col.hasClass('sortable')){
7948             return;
7949         }
7950         
7951         var sort = col.attr('sort');
7952         var dir = 'ASC';
7953         
7954         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7955             dir = 'DESC';
7956         }
7957         
7958         this.store.sortInfo = {field : sort, direction : dir};
7959         
7960         if (this.footer) {
7961             Roo.log("calling footer first");
7962             this.footer.onClick('first');
7963         } else {
7964         
7965             this.store.load({ params : { start : 0 } });
7966         }
7967     },
7968     
7969     renderHeader : function()
7970     {
7971         var header = {
7972             tag: 'thead',
7973             cn : []
7974         };
7975         
7976         var cm = this.cm;
7977         this.totalWidth = 0;
7978         
7979         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7980             
7981             var config = cm.config[i];
7982             
7983             var c = {
7984                 tag: 'th',
7985                 cls : 'x-hcol-' + i,
7986                 style : '',
7987                 html: cm.getColumnHeader(i)
7988             };
7989             
7990             var hh = '';
7991             
7992             if(typeof(config.sortable) != 'undefined' && config.sortable){
7993                 c.cls = 'sortable';
7994                 c.html = '<i class="glyphicon"></i>' + c.html;
7995             }
7996             
7997             // could use BS4 hidden-..-down 
7998             
7999             if(typeof(config.lgHeader) != 'undefined'){
8000                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8001             }
8002             
8003             if(typeof(config.mdHeader) != 'undefined'){
8004                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8005             }
8006             
8007             if(typeof(config.smHeader) != 'undefined'){
8008                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8009             }
8010             
8011             if(typeof(config.xsHeader) != 'undefined'){
8012                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8013             }
8014             
8015             if(hh.length){
8016                 c.html = hh;
8017             }
8018             
8019             if(typeof(config.tooltip) != 'undefined'){
8020                 c.tooltip = config.tooltip;
8021             }
8022             
8023             if(typeof(config.colspan) != 'undefined'){
8024                 c.colspan = config.colspan;
8025             }
8026             
8027             if(typeof(config.hidden) != 'undefined' && config.hidden){
8028                 c.style += ' display:none;';
8029             }
8030             
8031             if(typeof(config.dataIndex) != 'undefined'){
8032                 c.sort = config.dataIndex;
8033             }
8034             
8035            
8036             
8037             if(typeof(config.align) != 'undefined' && config.align.length){
8038                 c.style += ' text-align:' + config.align + ';';
8039             }
8040             
8041             if(typeof(config.width) != 'undefined'){
8042                 c.style += ' width:' + config.width + 'px;';
8043                 this.totalWidth += config.width;
8044             } else {
8045                 this.totalWidth += 100; // assume minimum of 100 per column?
8046             }
8047             
8048             if(typeof(config.cls) != 'undefined'){
8049                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8050             }
8051             
8052             ['xs','sm','md','lg'].map(function(size){
8053                 
8054                 if(typeof(config[size]) == 'undefined'){
8055                     return;
8056                 }
8057                  
8058                 if (!config[size]) { // 0 = hidden
8059                     // BS 4 '0' is treated as hide that column and below.
8060                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8061                     return;
8062                 }
8063                 
8064                 c.cls += ' col-' + size + '-' + config[size] + (
8065                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8066                 );
8067                 
8068                 
8069             });
8070             
8071             header.cn.push(c)
8072         }
8073         
8074         return header;
8075     },
8076     
8077     renderBody : function()
8078     {
8079         var body = {
8080             tag: 'tbody',
8081             cn : [
8082                 {
8083                     tag: 'tr',
8084                     cn : [
8085                         {
8086                             tag : 'td',
8087                             colspan :  this.cm.getColumnCount()
8088                         }
8089                     ]
8090                 }
8091             ]
8092         };
8093         
8094         return body;
8095     },
8096     
8097     renderFooter : function()
8098     {
8099         var footer = {
8100             tag: 'tfoot',
8101             cn : [
8102                 {
8103                     tag: 'tr',
8104                     cn : [
8105                         {
8106                             tag : 'td',
8107                             colspan :  this.cm.getColumnCount()
8108                         }
8109                     ]
8110                 }
8111             ]
8112         };
8113         
8114         return footer;
8115     },
8116     
8117     
8118     
8119     onLoad : function()
8120     {
8121 //        Roo.log('ds onload');
8122         this.clear();
8123         
8124         var _this = this;
8125         var cm = this.cm;
8126         var ds = this.store;
8127         
8128         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8129             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8130             if (_this.store.sortInfo) {
8131                     
8132                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8133                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8134                 }
8135                 
8136                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8137                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8138                 }
8139             }
8140         });
8141         
8142         var tbody =  this.mainBody;
8143               
8144         if(ds.getCount() > 0){
8145             ds.data.each(function(d,rowIndex){
8146                 var row =  this.renderRow(cm, ds, rowIndex);
8147                 
8148                 tbody.createChild(row);
8149                 
8150                 var _this = this;
8151                 
8152                 if(row.cellObjects.length){
8153                     Roo.each(row.cellObjects, function(r){
8154                         _this.renderCellObject(r);
8155                     })
8156                 }
8157                 
8158             }, this);
8159         }
8160         
8161         var tfoot = this.el.select('tfoot', true).first();
8162         
8163         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8164             
8165             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8166             
8167             var total = this.ds.getTotalCount();
8168             
8169             if(this.footer.pageSize < total){
8170                 this.mainFoot.show();
8171             }
8172         }
8173         
8174         Roo.each(this.el.select('tbody td', true).elements, function(e){
8175             e.on('mouseover', _this.onMouseover, _this);
8176         });
8177         
8178         Roo.each(this.el.select('tbody td', true).elements, function(e){
8179             e.on('mouseout', _this.onMouseout, _this);
8180         });
8181         this.fireEvent('rowsrendered', this);
8182         
8183         this.autoSize();
8184     },
8185     
8186     
8187     onUpdate : function(ds,record)
8188     {
8189         this.refreshRow(record);
8190         this.autoSize();
8191     },
8192     
8193     onRemove : function(ds, record, index, isUpdate){
8194         if(isUpdate !== true){
8195             this.fireEvent("beforerowremoved", this, index, record);
8196         }
8197         var bt = this.mainBody.dom;
8198         
8199         var rows = this.el.select('tbody > tr', true).elements;
8200         
8201         if(typeof(rows[index]) != 'undefined'){
8202             bt.removeChild(rows[index].dom);
8203         }
8204         
8205 //        if(bt.rows[index]){
8206 //            bt.removeChild(bt.rows[index]);
8207 //        }
8208         
8209         if(isUpdate !== true){
8210             //this.stripeRows(index);
8211             //this.syncRowHeights(index, index);
8212             //this.layout();
8213             this.fireEvent("rowremoved", this, index, record);
8214         }
8215     },
8216     
8217     onAdd : function(ds, records, rowIndex)
8218     {
8219         //Roo.log('on Add called');
8220         // - note this does not handle multiple adding very well..
8221         var bt = this.mainBody.dom;
8222         for (var i =0 ; i < records.length;i++) {
8223             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8224             //Roo.log(records[i]);
8225             //Roo.log(this.store.getAt(rowIndex+i));
8226             this.insertRow(this.store, rowIndex + i, false);
8227             return;
8228         }
8229         
8230     },
8231     
8232     
8233     refreshRow : function(record){
8234         var ds = this.store, index;
8235         if(typeof record == 'number'){
8236             index = record;
8237             record = ds.getAt(index);
8238         }else{
8239             index = ds.indexOf(record);
8240         }
8241         this.insertRow(ds, index, true);
8242         this.autoSize();
8243         this.onRemove(ds, record, index+1, true);
8244         this.autoSize();
8245         //this.syncRowHeights(index, index);
8246         //this.layout();
8247         this.fireEvent("rowupdated", this, index, record);
8248     },
8249     
8250     insertRow : function(dm, rowIndex, isUpdate){
8251         
8252         if(!isUpdate){
8253             this.fireEvent("beforerowsinserted", this, rowIndex);
8254         }
8255             //var s = this.getScrollState();
8256         var row = this.renderRow(this.cm, this.store, rowIndex);
8257         // insert before rowIndex..
8258         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8259         
8260         var _this = this;
8261                 
8262         if(row.cellObjects.length){
8263             Roo.each(row.cellObjects, function(r){
8264                 _this.renderCellObject(r);
8265             })
8266         }
8267             
8268         if(!isUpdate){
8269             this.fireEvent("rowsinserted", this, rowIndex);
8270             //this.syncRowHeights(firstRow, lastRow);
8271             //this.stripeRows(firstRow);
8272             //this.layout();
8273         }
8274         
8275     },
8276     
8277     
8278     getRowDom : function(rowIndex)
8279     {
8280         var rows = this.el.select('tbody > tr', true).elements;
8281         
8282         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8283         
8284     },
8285     // returns the object tree for a tr..
8286   
8287     
8288     renderRow : function(cm, ds, rowIndex) 
8289     {
8290         var d = ds.getAt(rowIndex);
8291         
8292         var row = {
8293             tag : 'tr',
8294             cls : 'x-row-' + rowIndex,
8295             cn : []
8296         };
8297             
8298         var cellObjects = [];
8299         
8300         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8301             var config = cm.config[i];
8302             
8303             var renderer = cm.getRenderer(i);
8304             var value = '';
8305             var id = false;
8306             
8307             if(typeof(renderer) !== 'undefined'){
8308                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8309             }
8310             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8311             // and are rendered into the cells after the row is rendered - using the id for the element.
8312             
8313             if(typeof(value) === 'object'){
8314                 id = Roo.id();
8315                 cellObjects.push({
8316                     container : id,
8317                     cfg : value 
8318                 })
8319             }
8320             
8321             var rowcfg = {
8322                 record: d,
8323                 rowIndex : rowIndex,
8324                 colIndex : i,
8325                 rowClass : ''
8326             };
8327
8328             this.fireEvent('rowclass', this, rowcfg);
8329             
8330             var td = {
8331                 tag: 'td',
8332                 cls : rowcfg.rowClass + ' x-col-' + i,
8333                 style: '',
8334                 html: (typeof(value) === 'object') ? '' : value
8335             };
8336             
8337             if (id) {
8338                 td.id = id;
8339             }
8340             
8341             if(typeof(config.colspan) != 'undefined'){
8342                 td.colspan = config.colspan;
8343             }
8344             
8345             if(typeof(config.hidden) != 'undefined' && config.hidden){
8346                 td.style += ' display:none;';
8347             }
8348             
8349             if(typeof(config.align) != 'undefined' && config.align.length){
8350                 td.style += ' text-align:' + config.align + ';';
8351             }
8352             if(typeof(config.valign) != 'undefined' && config.valign.length){
8353                 td.style += ' vertical-align:' + config.valign + ';';
8354             }
8355             
8356             if(typeof(config.width) != 'undefined'){
8357                 td.style += ' width:' +  config.width + 'px;';
8358             }
8359             
8360             if(typeof(config.cursor) != 'undefined'){
8361                 td.style += ' cursor:' +  config.cursor + ';';
8362             }
8363             
8364             if(typeof(config.cls) != 'undefined'){
8365                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8366             }
8367             
8368             ['xs','sm','md','lg'].map(function(size){
8369                 
8370                 if(typeof(config[size]) == 'undefined'){
8371                     return;
8372                 }
8373                 
8374                 
8375                   
8376                 if (!config[size]) { // 0 = hidden
8377                     // BS 4 '0' is treated as hide that column and below.
8378                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8379                     return;
8380                 }
8381                 
8382                 td.cls += ' col-' + size + '-' + config[size] + (
8383                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8384                 );
8385                  
8386
8387             });
8388             
8389             row.cn.push(td);
8390            
8391         }
8392         
8393         row.cellObjects = cellObjects;
8394         
8395         return row;
8396           
8397     },
8398     
8399     
8400     
8401     onBeforeLoad : function()
8402     {
8403         
8404     },
8405      /**
8406      * Remove all rows
8407      */
8408     clear : function()
8409     {
8410         this.el.select('tbody', true).first().dom.innerHTML = '';
8411     },
8412     /**
8413      * Show or hide a row.
8414      * @param {Number} rowIndex to show or hide
8415      * @param {Boolean} state hide
8416      */
8417     setRowVisibility : function(rowIndex, state)
8418     {
8419         var bt = this.mainBody.dom;
8420         
8421         var rows = this.el.select('tbody > tr', true).elements;
8422         
8423         if(typeof(rows[rowIndex]) == 'undefined'){
8424             return;
8425         }
8426         rows[rowIndex].dom.style.display = state ? '' : 'none';
8427     },
8428     
8429     
8430     getSelectionModel : function(){
8431         if(!this.selModel){
8432             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8433         }
8434         return this.selModel;
8435     },
8436     /*
8437      * Render the Roo.bootstrap object from renderder
8438      */
8439     renderCellObject : function(r)
8440     {
8441         var _this = this;
8442         
8443         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8444         
8445         var t = r.cfg.render(r.container);
8446         
8447         if(r.cfg.cn){
8448             Roo.each(r.cfg.cn, function(c){
8449                 var child = {
8450                     container: t.getChildContainer(),
8451                     cfg: c
8452                 };
8453                 _this.renderCellObject(child);
8454             })
8455         }
8456     },
8457     
8458     getRowIndex : function(row)
8459     {
8460         var rowIndex = -1;
8461         
8462         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8463             if(el != row){
8464                 return;
8465             }
8466             
8467             rowIndex = index;
8468         });
8469         
8470         return rowIndex;
8471     },
8472      /**
8473      * Returns the grid's underlying element = used by panel.Grid
8474      * @return {Element} The element
8475      */
8476     getGridEl : function(){
8477         return this.el;
8478     },
8479      /**
8480      * Forces a resize - used by panel.Grid
8481      * @return {Element} The element
8482      */
8483     autoSize : function()
8484     {
8485         //var ctr = Roo.get(this.container.dom.parentElement);
8486         var ctr = Roo.get(this.el.dom);
8487         
8488         var thd = this.getGridEl().select('thead',true).first();
8489         var tbd = this.getGridEl().select('tbody', true).first();
8490         var tfd = this.getGridEl().select('tfoot', true).first();
8491         
8492         var cw = ctr.getWidth();
8493         
8494         if (tbd) {
8495             
8496             tbd.setWidth(ctr.getWidth());
8497             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8498             // this needs fixing for various usage - currently only hydra job advers I think..
8499             //tdb.setHeight(
8500             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8501             //); 
8502             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8503             cw -= barsize;
8504         }
8505         cw = Math.max(cw, this.totalWidth);
8506         this.getGridEl().select('tr',true).setWidth(cw);
8507         // resize 'expandable coloumn?
8508         
8509         return; // we doe not have a view in this design..
8510         
8511     },
8512     onBodyScroll: function()
8513     {
8514         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8515         if(this.mainHead){
8516             this.mainHead.setStyle({
8517                 'position' : 'relative',
8518                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8519             });
8520         }
8521         
8522         if(this.lazyLoad){
8523             
8524             var scrollHeight = this.mainBody.dom.scrollHeight;
8525             
8526             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8527             
8528             var height = this.mainBody.getHeight();
8529             
8530             if(scrollHeight - height == scrollTop) {
8531                 
8532                 var total = this.ds.getTotalCount();
8533                 
8534                 if(this.footer.cursor + this.footer.pageSize < total){
8535                     
8536                     this.footer.ds.load({
8537                         params : {
8538                             start : this.footer.cursor + this.footer.pageSize,
8539                             limit : this.footer.pageSize
8540                         },
8541                         add : true
8542                     });
8543                 }
8544             }
8545             
8546         }
8547     },
8548     
8549     onHeaderChange : function()
8550     {
8551         var header = this.renderHeader();
8552         var table = this.el.select('table', true).first();
8553         
8554         this.mainHead.remove();
8555         this.mainHead = table.createChild(header, this.mainBody, false);
8556     },
8557     
8558     onHiddenChange : function(colModel, colIndex, hidden)
8559     {
8560         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8561         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8562         
8563         this.CSS.updateRule(thSelector, "display", "");
8564         this.CSS.updateRule(tdSelector, "display", "");
8565         
8566         if(hidden){
8567             this.CSS.updateRule(thSelector, "display", "none");
8568             this.CSS.updateRule(tdSelector, "display", "none");
8569         }
8570         
8571         this.onHeaderChange();
8572         this.onLoad();
8573     },
8574     
8575     setColumnWidth: function(col_index, width)
8576     {
8577         // width = "md-2 xs-2..."
8578         if(!this.colModel.config[col_index]) {
8579             return;
8580         }
8581         
8582         var w = width.split(" ");
8583         
8584         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8585         
8586         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8587         
8588         
8589         for(var j = 0; j < w.length; j++) {
8590             
8591             if(!w[j]) {
8592                 continue;
8593             }
8594             
8595             var size_cls = w[j].split("-");
8596             
8597             if(!Number.isInteger(size_cls[1] * 1)) {
8598                 continue;
8599             }
8600             
8601             if(!this.colModel.config[col_index][size_cls[0]]) {
8602                 continue;
8603             }
8604             
8605             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8606                 continue;
8607             }
8608             
8609             h_row[0].classList.replace(
8610                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8611                 "col-"+size_cls[0]+"-"+size_cls[1]
8612             );
8613             
8614             for(var i = 0; i < rows.length; i++) {
8615                 
8616                 var size_cls = w[j].split("-");
8617                 
8618                 if(!Number.isInteger(size_cls[1] * 1)) {
8619                     continue;
8620                 }
8621                 
8622                 if(!this.colModel.config[col_index][size_cls[0]]) {
8623                     continue;
8624                 }
8625                 
8626                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8627                     continue;
8628                 }
8629                 
8630                 rows[i].classList.replace(
8631                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8632                     "col-"+size_cls[0]+"-"+size_cls[1]
8633                 );
8634             }
8635             
8636             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8637         }
8638     }
8639 });
8640
8641  
8642
8643  /*
8644  * - LGPL
8645  *
8646  * table cell
8647  * 
8648  */
8649
8650 /**
8651  * @class Roo.bootstrap.TableCell
8652  * @extends Roo.bootstrap.Component
8653  * Bootstrap TableCell class
8654  * @cfg {String} html cell contain text
8655  * @cfg {String} cls cell class
8656  * @cfg {String} tag cell tag (td|th) default td
8657  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8658  * @cfg {String} align Aligns the content in a cell
8659  * @cfg {String} axis Categorizes cells
8660  * @cfg {String} bgcolor Specifies the background color of a cell
8661  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8662  * @cfg {Number} colspan Specifies the number of columns a cell should span
8663  * @cfg {String} headers Specifies one or more header cells a cell is related to
8664  * @cfg {Number} height Sets the height of a cell
8665  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8666  * @cfg {Number} rowspan Sets the number of rows a cell should span
8667  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8668  * @cfg {String} valign Vertical aligns the content in a cell
8669  * @cfg {Number} width Specifies the width of a cell
8670  * 
8671  * @constructor
8672  * Create a new TableCell
8673  * @param {Object} config The config object
8674  */
8675
8676 Roo.bootstrap.TableCell = function(config){
8677     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8678 };
8679
8680 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8681     
8682     html: false,
8683     cls: false,
8684     tag: false,
8685     abbr: false,
8686     align: false,
8687     axis: false,
8688     bgcolor: false,
8689     charoff: false,
8690     colspan: false,
8691     headers: false,
8692     height: false,
8693     nowrap: false,
8694     rowspan: false,
8695     scope: false,
8696     valign: false,
8697     width: false,
8698     
8699     
8700     getAutoCreate : function(){
8701         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8702         
8703         cfg = {
8704             tag: 'td'
8705         };
8706         
8707         if(this.tag){
8708             cfg.tag = this.tag;
8709         }
8710         
8711         if (this.html) {
8712             cfg.html=this.html
8713         }
8714         if (this.cls) {
8715             cfg.cls=this.cls
8716         }
8717         if (this.abbr) {
8718             cfg.abbr=this.abbr
8719         }
8720         if (this.align) {
8721             cfg.align=this.align
8722         }
8723         if (this.axis) {
8724             cfg.axis=this.axis
8725         }
8726         if (this.bgcolor) {
8727             cfg.bgcolor=this.bgcolor
8728         }
8729         if (this.charoff) {
8730             cfg.charoff=this.charoff
8731         }
8732         if (this.colspan) {
8733             cfg.colspan=this.colspan
8734         }
8735         if (this.headers) {
8736             cfg.headers=this.headers
8737         }
8738         if (this.height) {
8739             cfg.height=this.height
8740         }
8741         if (this.nowrap) {
8742             cfg.nowrap=this.nowrap
8743         }
8744         if (this.rowspan) {
8745             cfg.rowspan=this.rowspan
8746         }
8747         if (this.scope) {
8748             cfg.scope=this.scope
8749         }
8750         if (this.valign) {
8751             cfg.valign=this.valign
8752         }
8753         if (this.width) {
8754             cfg.width=this.width
8755         }
8756         
8757         
8758         return cfg;
8759     }
8760    
8761 });
8762
8763  
8764
8765  /*
8766  * - LGPL
8767  *
8768  * table row
8769  * 
8770  */
8771
8772 /**
8773  * @class Roo.bootstrap.TableRow
8774  * @extends Roo.bootstrap.Component
8775  * Bootstrap TableRow class
8776  * @cfg {String} cls row class
8777  * @cfg {String} align Aligns the content in a table row
8778  * @cfg {String} bgcolor Specifies a background color for a table row
8779  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8780  * @cfg {String} valign Vertical aligns the content in a table row
8781  * 
8782  * @constructor
8783  * Create a new TableRow
8784  * @param {Object} config The config object
8785  */
8786
8787 Roo.bootstrap.TableRow = function(config){
8788     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8789 };
8790
8791 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
8792     
8793     cls: false,
8794     align: false,
8795     bgcolor: false,
8796     charoff: false,
8797     valign: false,
8798     
8799     getAutoCreate : function(){
8800         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8801         
8802         cfg = {
8803             tag: 'tr'
8804         };
8805             
8806         if(this.cls){
8807             cfg.cls = this.cls;
8808         }
8809         if(this.align){
8810             cfg.align = this.align;
8811         }
8812         if(this.bgcolor){
8813             cfg.bgcolor = this.bgcolor;
8814         }
8815         if(this.charoff){
8816             cfg.charoff = this.charoff;
8817         }
8818         if(this.valign){
8819             cfg.valign = this.valign;
8820         }
8821         
8822         return cfg;
8823     }
8824    
8825 });
8826
8827  
8828
8829  /*
8830  * - LGPL
8831  *
8832  * table body
8833  * 
8834  */
8835
8836 /**
8837  * @class Roo.bootstrap.TableBody
8838  * @extends Roo.bootstrap.Component
8839  * Bootstrap TableBody class
8840  * @cfg {String} cls element class
8841  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8842  * @cfg {String} align Aligns the content inside the element
8843  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8844  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8845  * 
8846  * @constructor
8847  * Create a new TableBody
8848  * @param {Object} config The config object
8849  */
8850
8851 Roo.bootstrap.TableBody = function(config){
8852     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8853 };
8854
8855 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
8856     
8857     cls: false,
8858     tag: false,
8859     align: false,
8860     charoff: false,
8861     valign: false,
8862     
8863     getAutoCreate : function(){
8864         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8865         
8866         cfg = {
8867             tag: 'tbody'
8868         };
8869             
8870         if (this.cls) {
8871             cfg.cls=this.cls
8872         }
8873         if(this.tag){
8874             cfg.tag = this.tag;
8875         }
8876         
8877         if(this.align){
8878             cfg.align = this.align;
8879         }
8880         if(this.charoff){
8881             cfg.charoff = this.charoff;
8882         }
8883         if(this.valign){
8884             cfg.valign = this.valign;
8885         }
8886         
8887         return cfg;
8888     }
8889     
8890     
8891 //    initEvents : function()
8892 //    {
8893 //        
8894 //        if(!this.store){
8895 //            return;
8896 //        }
8897 //        
8898 //        this.store = Roo.factory(this.store, Roo.data);
8899 //        this.store.on('load', this.onLoad, this);
8900 //        
8901 //        this.store.load();
8902 //        
8903 //    },
8904 //    
8905 //    onLoad: function () 
8906 //    {   
8907 //        this.fireEvent('load', this);
8908 //    }
8909 //    
8910 //   
8911 });
8912
8913  
8914
8915  /*
8916  * Based on:
8917  * Ext JS Library 1.1.1
8918  * Copyright(c) 2006-2007, Ext JS, LLC.
8919  *
8920  * Originally Released Under LGPL - original licence link has changed is not relivant.
8921  *
8922  * Fork - LGPL
8923  * <script type="text/javascript">
8924  */
8925
8926 // as we use this in bootstrap.
8927 Roo.namespace('Roo.form');
8928  /**
8929  * @class Roo.form.Action
8930  * Internal Class used to handle form actions
8931  * @constructor
8932  * @param {Roo.form.BasicForm} el The form element or its id
8933  * @param {Object} config Configuration options
8934  */
8935
8936  
8937  
8938 // define the action interface
8939 Roo.form.Action = function(form, options){
8940     this.form = form;
8941     this.options = options || {};
8942 };
8943 /**
8944  * Client Validation Failed
8945  * @const 
8946  */
8947 Roo.form.Action.CLIENT_INVALID = 'client';
8948 /**
8949  * Server Validation Failed
8950  * @const 
8951  */
8952 Roo.form.Action.SERVER_INVALID = 'server';
8953  /**
8954  * Connect to Server Failed
8955  * @const 
8956  */
8957 Roo.form.Action.CONNECT_FAILURE = 'connect';
8958 /**
8959  * Reading Data from Server Failed
8960  * @const 
8961  */
8962 Roo.form.Action.LOAD_FAILURE = 'load';
8963
8964 Roo.form.Action.prototype = {
8965     type : 'default',
8966     failureType : undefined,
8967     response : undefined,
8968     result : undefined,
8969
8970     // interface method
8971     run : function(options){
8972
8973     },
8974
8975     // interface method
8976     success : function(response){
8977
8978     },
8979
8980     // interface method
8981     handleResponse : function(response){
8982
8983     },
8984
8985     // default connection failure
8986     failure : function(response){
8987         
8988         this.response = response;
8989         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8990         this.form.afterAction(this, false);
8991     },
8992
8993     processResponse : function(response){
8994         this.response = response;
8995         if(!response.responseText){
8996             return true;
8997         }
8998         this.result = this.handleResponse(response);
8999         return this.result;
9000     },
9001
9002     // utility functions used internally
9003     getUrl : function(appendParams){
9004         var url = this.options.url || this.form.url || this.form.el.dom.action;
9005         if(appendParams){
9006             var p = this.getParams();
9007             if(p){
9008                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9009             }
9010         }
9011         return url;
9012     },
9013
9014     getMethod : function(){
9015         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9016     },
9017
9018     getParams : function(){
9019         var bp = this.form.baseParams;
9020         var p = this.options.params;
9021         if(p){
9022             if(typeof p == "object"){
9023                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9024             }else if(typeof p == 'string' && bp){
9025                 p += '&' + Roo.urlEncode(bp);
9026             }
9027         }else if(bp){
9028             p = Roo.urlEncode(bp);
9029         }
9030         return p;
9031     },
9032
9033     createCallback : function(){
9034         return {
9035             success: this.success,
9036             failure: this.failure,
9037             scope: this,
9038             timeout: (this.form.timeout*1000),
9039             upload: this.form.fileUpload ? this.success : undefined
9040         };
9041     }
9042 };
9043
9044 Roo.form.Action.Submit = function(form, options){
9045     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9046 };
9047
9048 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9049     type : 'submit',
9050
9051     haveProgress : false,
9052     uploadComplete : false,
9053     
9054     // uploadProgress indicator.
9055     uploadProgress : function()
9056     {
9057         if (!this.form.progressUrl) {
9058             return;
9059         }
9060         
9061         if (!this.haveProgress) {
9062             Roo.MessageBox.progress("Uploading", "Uploading");
9063         }
9064         if (this.uploadComplete) {
9065            Roo.MessageBox.hide();
9066            return;
9067         }
9068         
9069         this.haveProgress = true;
9070    
9071         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9072         
9073         var c = new Roo.data.Connection();
9074         c.request({
9075             url : this.form.progressUrl,
9076             params: {
9077                 id : uid
9078             },
9079             method: 'GET',
9080             success : function(req){
9081                //console.log(data);
9082                 var rdata = false;
9083                 var edata;
9084                 try  {
9085                    rdata = Roo.decode(req.responseText)
9086                 } catch (e) {
9087                     Roo.log("Invalid data from server..");
9088                     Roo.log(edata);
9089                     return;
9090                 }
9091                 if (!rdata || !rdata.success) {
9092                     Roo.log(rdata);
9093                     Roo.MessageBox.alert(Roo.encode(rdata));
9094                     return;
9095                 }
9096                 var data = rdata.data;
9097                 
9098                 if (this.uploadComplete) {
9099                    Roo.MessageBox.hide();
9100                    return;
9101                 }
9102                    
9103                 if (data){
9104                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9105                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9106                     );
9107                 }
9108                 this.uploadProgress.defer(2000,this);
9109             },
9110        
9111             failure: function(data) {
9112                 Roo.log('progress url failed ');
9113                 Roo.log(data);
9114             },
9115             scope : this
9116         });
9117            
9118     },
9119     
9120     
9121     run : function()
9122     {
9123         // run get Values on the form, so it syncs any secondary forms.
9124         this.form.getValues();
9125         
9126         var o = this.options;
9127         var method = this.getMethod();
9128         var isPost = method == 'POST';
9129         if(o.clientValidation === false || this.form.isValid()){
9130             
9131             if (this.form.progressUrl) {
9132                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9133                     (new Date() * 1) + '' + Math.random());
9134                     
9135             } 
9136             
9137             
9138             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9139                 form:this.form.el.dom,
9140                 url:this.getUrl(!isPost),
9141                 method: method,
9142                 params:isPost ? this.getParams() : null,
9143                 isUpload: this.form.fileUpload,
9144                 formData : this.form.formData
9145             }));
9146             
9147             this.uploadProgress();
9148
9149         }else if (o.clientValidation !== false){ // client validation failed
9150             this.failureType = Roo.form.Action.CLIENT_INVALID;
9151             this.form.afterAction(this, false);
9152         }
9153     },
9154
9155     success : function(response)
9156     {
9157         this.uploadComplete= true;
9158         if (this.haveProgress) {
9159             Roo.MessageBox.hide();
9160         }
9161         
9162         
9163         var result = this.processResponse(response);
9164         if(result === true || result.success){
9165             this.form.afterAction(this, true);
9166             return;
9167         }
9168         if(result.errors){
9169             this.form.markInvalid(result.errors);
9170             this.failureType = Roo.form.Action.SERVER_INVALID;
9171         }
9172         this.form.afterAction(this, false);
9173     },
9174     failure : function(response)
9175     {
9176         this.uploadComplete= true;
9177         if (this.haveProgress) {
9178             Roo.MessageBox.hide();
9179         }
9180         
9181         this.response = response;
9182         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9183         this.form.afterAction(this, false);
9184     },
9185     
9186     handleResponse : function(response){
9187         if(this.form.errorReader){
9188             var rs = this.form.errorReader.read(response);
9189             var errors = [];
9190             if(rs.records){
9191                 for(var i = 0, len = rs.records.length; i < len; i++) {
9192                     var r = rs.records[i];
9193                     errors[i] = r.data;
9194                 }
9195             }
9196             if(errors.length < 1){
9197                 errors = null;
9198             }
9199             return {
9200                 success : rs.success,
9201                 errors : errors
9202             };
9203         }
9204         var ret = false;
9205         try {
9206             ret = Roo.decode(response.responseText);
9207         } catch (e) {
9208             ret = {
9209                 success: false,
9210                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9211                 errors : []
9212             };
9213         }
9214         return ret;
9215         
9216     }
9217 });
9218
9219
9220 Roo.form.Action.Load = function(form, options){
9221     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9222     this.reader = this.form.reader;
9223 };
9224
9225 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9226     type : 'load',
9227
9228     run : function(){
9229         
9230         Roo.Ajax.request(Roo.apply(
9231                 this.createCallback(), {
9232                     method:this.getMethod(),
9233                     url:this.getUrl(false),
9234                     params:this.getParams()
9235         }));
9236     },
9237
9238     success : function(response){
9239         
9240         var result = this.processResponse(response);
9241         if(result === true || !result.success || !result.data){
9242             this.failureType = Roo.form.Action.LOAD_FAILURE;
9243             this.form.afterAction(this, false);
9244             return;
9245         }
9246         this.form.clearInvalid();
9247         this.form.setValues(result.data);
9248         this.form.afterAction(this, true);
9249     },
9250
9251     handleResponse : function(response){
9252         if(this.form.reader){
9253             var rs = this.form.reader.read(response);
9254             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9255             return {
9256                 success : rs.success,
9257                 data : data
9258             };
9259         }
9260         return Roo.decode(response.responseText);
9261     }
9262 });
9263
9264 Roo.form.Action.ACTION_TYPES = {
9265     'load' : Roo.form.Action.Load,
9266     'submit' : Roo.form.Action.Submit
9267 };/*
9268  * - LGPL
9269  *
9270  * form
9271  *
9272  */
9273
9274 /**
9275  * @class Roo.bootstrap.Form
9276  * @extends Roo.bootstrap.Component
9277  * Bootstrap Form class
9278  * @cfg {String} method  GET | POST (default POST)
9279  * @cfg {String} labelAlign top | left (default top)
9280  * @cfg {String} align left  | right - for navbars
9281  * @cfg {Boolean} loadMask load mask when submit (default true)
9282
9283  *
9284  * @constructor
9285  * Create a new Form
9286  * @param {Object} config The config object
9287  */
9288
9289
9290 Roo.bootstrap.Form = function(config){
9291     
9292     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9293     
9294     Roo.bootstrap.Form.popover.apply();
9295     
9296     this.addEvents({
9297         /**
9298          * @event clientvalidation
9299          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9300          * @param {Form} this
9301          * @param {Boolean} valid true if the form has passed client-side validation
9302          */
9303         clientvalidation: true,
9304         /**
9305          * @event beforeaction
9306          * Fires before any action is performed. Return false to cancel the action.
9307          * @param {Form} this
9308          * @param {Action} action The action to be performed
9309          */
9310         beforeaction: true,
9311         /**
9312          * @event actionfailed
9313          * Fires when an action fails.
9314          * @param {Form} this
9315          * @param {Action} action The action that failed
9316          */
9317         actionfailed : true,
9318         /**
9319          * @event actioncomplete
9320          * Fires when an action is completed.
9321          * @param {Form} this
9322          * @param {Action} action The action that completed
9323          */
9324         actioncomplete : true
9325     });
9326 };
9327
9328 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9329
9330      /**
9331      * @cfg {String} method
9332      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9333      */
9334     method : 'POST',
9335     /**
9336      * @cfg {String} url
9337      * The URL to use for form actions if one isn't supplied in the action options.
9338      */
9339     /**
9340      * @cfg {Boolean} fileUpload
9341      * Set to true if this form is a file upload.
9342      */
9343
9344     /**
9345      * @cfg {Object} baseParams
9346      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9347      */
9348
9349     /**
9350      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9351      */
9352     timeout: 30,
9353     /**
9354      * @cfg {Sting} align (left|right) for navbar forms
9355      */
9356     align : 'left',
9357
9358     // private
9359     activeAction : null,
9360
9361     /**
9362      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9363      * element by passing it or its id or mask the form itself by passing in true.
9364      * @type Mixed
9365      */
9366     waitMsgTarget : false,
9367
9368     loadMask : true,
9369     
9370     /**
9371      * @cfg {Boolean} errorMask (true|false) default false
9372      */
9373     errorMask : false,
9374     
9375     /**
9376      * @cfg {Number} maskOffset Default 100
9377      */
9378     maskOffset : 100,
9379     
9380     /**
9381      * @cfg {Boolean} maskBody
9382      */
9383     maskBody : false,
9384
9385     getAutoCreate : function(){
9386
9387         var cfg = {
9388             tag: 'form',
9389             method : this.method || 'POST',
9390             id : this.id || Roo.id(),
9391             cls : ''
9392         };
9393         if (this.parent().xtype.match(/^Nav/)) {
9394             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9395
9396         }
9397
9398         if (this.labelAlign == 'left' ) {
9399             cfg.cls += ' form-horizontal';
9400         }
9401
9402
9403         return cfg;
9404     },
9405     initEvents : function()
9406     {
9407         this.el.on('submit', this.onSubmit, this);
9408         // this was added as random key presses on the form where triggering form submit.
9409         this.el.on('keypress', function(e) {
9410             if (e.getCharCode() != 13) {
9411                 return true;
9412             }
9413             // we might need to allow it for textareas.. and some other items.
9414             // check e.getTarget().
9415
9416             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9417                 return true;
9418             }
9419
9420             Roo.log("keypress blocked");
9421
9422             e.preventDefault();
9423             return false;
9424         });
9425         
9426     },
9427     // private
9428     onSubmit : function(e){
9429         e.stopEvent();
9430     },
9431
9432      /**
9433      * Returns true if client-side validation on the form is successful.
9434      * @return Boolean
9435      */
9436     isValid : function(){
9437         var items = this.getItems();
9438         var valid = true;
9439         var target = false;
9440         
9441         items.each(function(f){
9442             
9443             if(f.validate()){
9444                 return;
9445             }
9446             
9447             Roo.log('invalid field: ' + f.name);
9448             
9449             valid = false;
9450
9451             if(!target && f.el.isVisible(true)){
9452                 target = f;
9453             }
9454            
9455         });
9456         
9457         if(this.errorMask && !valid){
9458             Roo.bootstrap.Form.popover.mask(this, target);
9459         }
9460         
9461         return valid;
9462     },
9463     
9464     /**
9465      * Returns true if any fields in this form have changed since their original load.
9466      * @return Boolean
9467      */
9468     isDirty : function(){
9469         var dirty = false;
9470         var items = this.getItems();
9471         items.each(function(f){
9472            if(f.isDirty()){
9473                dirty = true;
9474                return false;
9475            }
9476            return true;
9477         });
9478         return dirty;
9479     },
9480      /**
9481      * Performs a predefined action (submit or load) or custom actions you define on this form.
9482      * @param {String} actionName The name of the action type
9483      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9484      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9485      * accept other config options):
9486      * <pre>
9487 Property          Type             Description
9488 ----------------  ---------------  ----------------------------------------------------------------------------------
9489 url               String           The url for the action (defaults to the form's url)
9490 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9491 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9492 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9493                                    validate the form on the client (defaults to false)
9494      * </pre>
9495      * @return {BasicForm} this
9496      */
9497     doAction : function(action, options){
9498         if(typeof action == 'string'){
9499             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9500         }
9501         if(this.fireEvent('beforeaction', this, action) !== false){
9502             this.beforeAction(action);
9503             action.run.defer(100, action);
9504         }
9505         return this;
9506     },
9507
9508     // private
9509     beforeAction : function(action){
9510         var o = action.options;
9511         
9512         if(this.loadMask){
9513             
9514             if(this.maskBody){
9515                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9516             } else {
9517                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9518             }
9519         }
9520         // not really supported yet.. ??
9521
9522         //if(this.waitMsgTarget === true){
9523         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9524         //}else if(this.waitMsgTarget){
9525         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9526         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9527         //}else {
9528         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9529        // }
9530
9531     },
9532
9533     // private
9534     afterAction : function(action, success){
9535         this.activeAction = null;
9536         var o = action.options;
9537
9538         if(this.loadMask){
9539             
9540             if(this.maskBody){
9541                 Roo.get(document.body).unmask();
9542             } else {
9543                 this.el.unmask();
9544             }
9545         }
9546         
9547         //if(this.waitMsgTarget === true){
9548 //            this.el.unmask();
9549         //}else if(this.waitMsgTarget){
9550         //    this.waitMsgTarget.unmask();
9551         //}else{
9552         //    Roo.MessageBox.updateProgress(1);
9553         //    Roo.MessageBox.hide();
9554        // }
9555         //
9556         if(success){
9557             if(o.reset){
9558                 this.reset();
9559             }
9560             Roo.callback(o.success, o.scope, [this, action]);
9561             this.fireEvent('actioncomplete', this, action);
9562
9563         }else{
9564
9565             // failure condition..
9566             // we have a scenario where updates need confirming.
9567             // eg. if a locking scenario exists..
9568             // we look for { errors : { needs_confirm : true }} in the response.
9569             if (
9570                 (typeof(action.result) != 'undefined')  &&
9571                 (typeof(action.result.errors) != 'undefined')  &&
9572                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9573            ){
9574                 var _t = this;
9575                 Roo.log("not supported yet");
9576                  /*
9577
9578                 Roo.MessageBox.confirm(
9579                     "Change requires confirmation",
9580                     action.result.errorMsg,
9581                     function(r) {
9582                         if (r != 'yes') {
9583                             return;
9584                         }
9585                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9586                     }
9587
9588                 );
9589                 */
9590
9591
9592                 return;
9593             }
9594
9595             Roo.callback(o.failure, o.scope, [this, action]);
9596             // show an error message if no failed handler is set..
9597             if (!this.hasListener('actionfailed')) {
9598                 Roo.log("need to add dialog support");
9599                 /*
9600                 Roo.MessageBox.alert("Error",
9601                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9602                         action.result.errorMsg :
9603                         "Saving Failed, please check your entries or try again"
9604                 );
9605                 */
9606             }
9607
9608             this.fireEvent('actionfailed', this, action);
9609         }
9610
9611     },
9612     /**
9613      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9614      * @param {String} id The value to search for
9615      * @return Field
9616      */
9617     findField : function(id){
9618         var items = this.getItems();
9619         var field = items.get(id);
9620         if(!field){
9621              items.each(function(f){
9622                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9623                     field = f;
9624                     return false;
9625                 }
9626                 return true;
9627             });
9628         }
9629         return field || null;
9630     },
9631      /**
9632      * Mark fields in this form invalid in bulk.
9633      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9634      * @return {BasicForm} this
9635      */
9636     markInvalid : function(errors){
9637         if(errors instanceof Array){
9638             for(var i = 0, len = errors.length; i < len; i++){
9639                 var fieldError = errors[i];
9640                 var f = this.findField(fieldError.id);
9641                 if(f){
9642                     f.markInvalid(fieldError.msg);
9643                 }
9644             }
9645         }else{
9646             var field, id;
9647             for(id in errors){
9648                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9649                     field.markInvalid(errors[id]);
9650                 }
9651             }
9652         }
9653         //Roo.each(this.childForms || [], function (f) {
9654         //    f.markInvalid(errors);
9655         //});
9656
9657         return this;
9658     },
9659
9660     /**
9661      * Set values for fields in this form in bulk.
9662      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9663      * @return {BasicForm} this
9664      */
9665     setValues : function(values){
9666         if(values instanceof Array){ // array of objects
9667             for(var i = 0, len = values.length; i < len; i++){
9668                 var v = values[i];
9669                 var f = this.findField(v.id);
9670                 if(f){
9671                     f.setValue(v.value);
9672                     if(this.trackResetOnLoad){
9673                         f.originalValue = f.getValue();
9674                     }
9675                 }
9676             }
9677         }else{ // object hash
9678             var field, id;
9679             for(id in values){
9680                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9681
9682                     if (field.setFromData &&
9683                         field.valueField &&
9684                         field.displayField &&
9685                         // combos' with local stores can
9686                         // be queried via setValue()
9687                         // to set their value..
9688                         (field.store && !field.store.isLocal)
9689                         ) {
9690                         // it's a combo
9691                         var sd = { };
9692                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9693                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9694                         field.setFromData(sd);
9695
9696                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9697                         
9698                         field.setFromData(values);
9699                         
9700                     } else {
9701                         field.setValue(values[id]);
9702                     }
9703
9704
9705                     if(this.trackResetOnLoad){
9706                         field.originalValue = field.getValue();
9707                     }
9708                 }
9709             }
9710         }
9711
9712         //Roo.each(this.childForms || [], function (f) {
9713         //    f.setValues(values);
9714         //});
9715
9716         return this;
9717     },
9718
9719     /**
9720      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9721      * they are returned as an array.
9722      * @param {Boolean} asString
9723      * @return {Object}
9724      */
9725     getValues : function(asString){
9726         //if (this.childForms) {
9727             // copy values from the child forms
9728         //    Roo.each(this.childForms, function (f) {
9729         //        this.setValues(f.getValues());
9730         //    }, this);
9731         //}
9732
9733
9734
9735         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9736         if(asString === true){
9737             return fs;
9738         }
9739         return Roo.urlDecode(fs);
9740     },
9741
9742     /**
9743      * Returns the fields in this form as an object with key/value pairs.
9744      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9745      * @return {Object}
9746      */
9747     getFieldValues : function(with_hidden)
9748     {
9749         var items = this.getItems();
9750         var ret = {};
9751         items.each(function(f){
9752             
9753             if (!f.getName()) {
9754                 return;
9755             }
9756             
9757             var v = f.getValue();
9758             
9759             if (f.inputType =='radio') {
9760                 if (typeof(ret[f.getName()]) == 'undefined') {
9761                     ret[f.getName()] = ''; // empty..
9762                 }
9763
9764                 if (!f.el.dom.checked) {
9765                     return;
9766
9767                 }
9768                 v = f.el.dom.value;
9769
9770             }
9771             
9772             if(f.xtype == 'MoneyField'){
9773                 ret[f.currencyName] = f.getCurrency();
9774             }
9775
9776             // not sure if this supported any more..
9777             if ((typeof(v) == 'object') && f.getRawValue) {
9778                 v = f.getRawValue() ; // dates..
9779             }
9780             // combo boxes where name != hiddenName...
9781             if (f.name !== false && f.name != '' && f.name != f.getName()) {
9782                 ret[f.name] = f.getRawValue();
9783             }
9784             ret[f.getName()] = v;
9785         });
9786
9787         return ret;
9788     },
9789
9790     /**
9791      * Clears all invalid messages in this form.
9792      * @return {BasicForm} this
9793      */
9794     clearInvalid : function(){
9795         var items = this.getItems();
9796
9797         items.each(function(f){
9798            f.clearInvalid();
9799         });
9800
9801         return this;
9802     },
9803
9804     /**
9805      * Resets this form.
9806      * @return {BasicForm} this
9807      */
9808     reset : function(){
9809         var items = this.getItems();
9810         items.each(function(f){
9811             f.reset();
9812         });
9813
9814         Roo.each(this.childForms || [], function (f) {
9815             f.reset();
9816         });
9817
9818
9819         return this;
9820     },
9821     
9822     getItems : function()
9823     {
9824         var r=new Roo.util.MixedCollection(false, function(o){
9825             return o.id || (o.id = Roo.id());
9826         });
9827         var iter = function(el) {
9828             if (el.inputEl) {
9829                 r.add(el);
9830             }
9831             if (!el.items) {
9832                 return;
9833             }
9834             Roo.each(el.items,function(e) {
9835                 iter(e);
9836             });
9837         };
9838
9839         iter(this);
9840         return r;
9841     },
9842     
9843     hideFields : function(items)
9844     {
9845         Roo.each(items, function(i){
9846             
9847             var f = this.findField(i);
9848             
9849             if(!f){
9850                 return;
9851             }
9852             
9853             f.hide();
9854             
9855         }, this);
9856     },
9857     
9858     showFields : function(items)
9859     {
9860         Roo.each(items, function(i){
9861             
9862             var f = this.findField(i);
9863             
9864             if(!f){
9865                 return;
9866             }
9867             
9868             f.show();
9869             
9870         }, this);
9871     }
9872
9873 });
9874
9875 Roo.apply(Roo.bootstrap.Form, {
9876     
9877     popover : {
9878         
9879         padding : 5,
9880         
9881         isApplied : false,
9882         
9883         isMasked : false,
9884         
9885         form : false,
9886         
9887         target : false,
9888         
9889         toolTip : false,
9890         
9891         intervalID : false,
9892         
9893         maskEl : false,
9894         
9895         apply : function()
9896         {
9897             if(this.isApplied){
9898                 return;
9899             }
9900             
9901             this.maskEl = {
9902                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9903                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
9904                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
9905                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
9906             };
9907             
9908             this.maskEl.top.enableDisplayMode("block");
9909             this.maskEl.left.enableDisplayMode("block");
9910             this.maskEl.bottom.enableDisplayMode("block");
9911             this.maskEl.right.enableDisplayMode("block");
9912             
9913             this.toolTip = new Roo.bootstrap.Tooltip({
9914                 cls : 'roo-form-error-popover',
9915                 alignment : {
9916                     'left' : ['r-l', [-2,0], 'right'],
9917                     'right' : ['l-r', [2,0], 'left'],
9918                     'bottom' : ['tl-bl', [0,2], 'top'],
9919                     'top' : [ 'bl-tl', [0,-2], 'bottom']
9920                 }
9921             });
9922             
9923             this.toolTip.render(Roo.get(document.body));
9924
9925             this.toolTip.el.enableDisplayMode("block");
9926             
9927             Roo.get(document.body).on('click', function(){
9928                 this.unmask();
9929             }, this);
9930             
9931             Roo.get(document.body).on('touchstart', function(){
9932                 this.unmask();
9933             }, this);
9934             
9935             this.isApplied = true
9936         },
9937         
9938         mask : function(form, target)
9939         {
9940             this.form = form;
9941             
9942             this.target = target;
9943             
9944             if(!this.form.errorMask || !target.el){
9945                 return;
9946             }
9947             
9948             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9949             
9950             Roo.log(scrollable);
9951             
9952             var ot = this.target.el.calcOffsetsTo(scrollable);
9953             
9954             var scrollTo = ot[1] - this.form.maskOffset;
9955             
9956             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9957             
9958             scrollable.scrollTo('top', scrollTo);
9959             
9960             var box = this.target.el.getBox();
9961             Roo.log(box);
9962             var zIndex = Roo.bootstrap.Modal.zIndex++;
9963
9964             
9965             this.maskEl.top.setStyle('position', 'absolute');
9966             this.maskEl.top.setStyle('z-index', zIndex);
9967             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
9968             this.maskEl.top.setLeft(0);
9969             this.maskEl.top.setTop(0);
9970             this.maskEl.top.show();
9971             
9972             this.maskEl.left.setStyle('position', 'absolute');
9973             this.maskEl.left.setStyle('z-index', zIndex);
9974             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
9975             this.maskEl.left.setLeft(0);
9976             this.maskEl.left.setTop(box.y - this.padding);
9977             this.maskEl.left.show();
9978
9979             this.maskEl.bottom.setStyle('position', 'absolute');
9980             this.maskEl.bottom.setStyle('z-index', zIndex);
9981             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
9982             this.maskEl.bottom.setLeft(0);
9983             this.maskEl.bottom.setTop(box.bottom + this.padding);
9984             this.maskEl.bottom.show();
9985
9986             this.maskEl.right.setStyle('position', 'absolute');
9987             this.maskEl.right.setStyle('z-index', zIndex);
9988             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
9989             this.maskEl.right.setLeft(box.right + this.padding);
9990             this.maskEl.right.setTop(box.y - this.padding);
9991             this.maskEl.right.show();
9992
9993             this.toolTip.bindEl = this.target.el;
9994
9995             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
9996
9997             var tip = this.target.blankText;
9998
9999             if(this.target.getValue() !== '' ) {
10000                 
10001                 if (this.target.invalidText.length) {
10002                     tip = this.target.invalidText;
10003                 } else if (this.target.regexText.length){
10004                     tip = this.target.regexText;
10005                 }
10006             }
10007
10008             this.toolTip.show(tip);
10009
10010             this.intervalID = window.setInterval(function() {
10011                 Roo.bootstrap.Form.popover.unmask();
10012             }, 10000);
10013
10014             window.onwheel = function(){ return false;};
10015             
10016             (function(){ this.isMasked = true; }).defer(500, this);
10017             
10018         },
10019         
10020         unmask : function()
10021         {
10022             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10023                 return;
10024             }
10025             
10026             this.maskEl.top.setStyle('position', 'absolute');
10027             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10028             this.maskEl.top.hide();
10029
10030             this.maskEl.left.setStyle('position', 'absolute');
10031             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10032             this.maskEl.left.hide();
10033
10034             this.maskEl.bottom.setStyle('position', 'absolute');
10035             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10036             this.maskEl.bottom.hide();
10037
10038             this.maskEl.right.setStyle('position', 'absolute');
10039             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10040             this.maskEl.right.hide();
10041             
10042             this.toolTip.hide();
10043             
10044             this.toolTip.el.hide();
10045             
10046             window.onwheel = function(){ return true;};
10047             
10048             if(this.intervalID){
10049                 window.clearInterval(this.intervalID);
10050                 this.intervalID = false;
10051             }
10052             
10053             this.isMasked = false;
10054             
10055         }
10056         
10057     }
10058     
10059 });
10060
10061 /*
10062  * Based on:
10063  * Ext JS Library 1.1.1
10064  * Copyright(c) 2006-2007, Ext JS, LLC.
10065  *
10066  * Originally Released Under LGPL - original licence link has changed is not relivant.
10067  *
10068  * Fork - LGPL
10069  * <script type="text/javascript">
10070  */
10071 /**
10072  * @class Roo.form.VTypes
10073  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10074  * @singleton
10075  */
10076 Roo.form.VTypes = function(){
10077     // closure these in so they are only created once.
10078     var alpha = /^[a-zA-Z_]+$/;
10079     var alphanum = /^[a-zA-Z0-9_]+$/;
10080     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10081     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10082
10083     // All these messages and functions are configurable
10084     return {
10085         /**
10086          * The function used to validate email addresses
10087          * @param {String} value The email address
10088          */
10089         'email' : function(v){
10090             return email.test(v);
10091         },
10092         /**
10093          * The error text to display when the email validation function returns false
10094          * @type String
10095          */
10096         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10097         /**
10098          * The keystroke filter mask to be applied on email input
10099          * @type RegExp
10100          */
10101         'emailMask' : /[a-z0-9_\.\-@]/i,
10102
10103         /**
10104          * The function used to validate URLs
10105          * @param {String} value The URL
10106          */
10107         'url' : function(v){
10108             return url.test(v);
10109         },
10110         /**
10111          * The error text to display when the url validation function returns false
10112          * @type String
10113          */
10114         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10115         
10116         /**
10117          * The function used to validate alpha values
10118          * @param {String} value The value
10119          */
10120         'alpha' : function(v){
10121             return alpha.test(v);
10122         },
10123         /**
10124          * The error text to display when the alpha validation function returns false
10125          * @type String
10126          */
10127         'alphaText' : 'This field should only contain letters and _',
10128         /**
10129          * The keystroke filter mask to be applied on alpha input
10130          * @type RegExp
10131          */
10132         'alphaMask' : /[a-z_]/i,
10133
10134         /**
10135          * The function used to validate alphanumeric values
10136          * @param {String} value The value
10137          */
10138         'alphanum' : function(v){
10139             return alphanum.test(v);
10140         },
10141         /**
10142          * The error text to display when the alphanumeric validation function returns false
10143          * @type String
10144          */
10145         'alphanumText' : 'This field should only contain letters, numbers and _',
10146         /**
10147          * The keystroke filter mask to be applied on alphanumeric input
10148          * @type RegExp
10149          */
10150         'alphanumMask' : /[a-z0-9_]/i
10151     };
10152 }();/*
10153  * - LGPL
10154  *
10155  * Input
10156  * 
10157  */
10158
10159 /**
10160  * @class Roo.bootstrap.Input
10161  * @extends Roo.bootstrap.Component
10162  * Bootstrap Input class
10163  * @cfg {Boolean} disabled is it disabled
10164  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
10165  * @cfg {String} name name of the input
10166  * @cfg {string} fieldLabel - the label associated
10167  * @cfg {string} placeholder - placeholder to put in text.
10168  * @cfg {string}  before - input group add on before
10169  * @cfg {string} after - input group add on after
10170  * @cfg {string} size - (lg|sm) or leave empty..
10171  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10172  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10173  * @cfg {Number} md colspan out of 12 for computer-sized screens
10174  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10175  * @cfg {string} value default value of the input
10176  * @cfg {Number} labelWidth set the width of label 
10177  * @cfg {Number} labellg set the width of label (1-12)
10178  * @cfg {Number} labelmd set the width of label (1-12)
10179  * @cfg {Number} labelsm set the width of label (1-12)
10180  * @cfg {Number} labelxs set the width of label (1-12)
10181  * @cfg {String} labelAlign (top|left)
10182  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10183  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10184  * @cfg {String} indicatorpos (left|right) default left
10185  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10186  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10187  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10188
10189  * @cfg {String} align (left|center|right) Default left
10190  * @cfg {Boolean} forceFeedback (true|false) Default false
10191  * 
10192  * @constructor
10193  * Create a new Input
10194  * @param {Object} config The config object
10195  */
10196
10197 Roo.bootstrap.Input = function(config){
10198     
10199     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10200     
10201     this.addEvents({
10202         /**
10203          * @event focus
10204          * Fires when this field receives input focus.
10205          * @param {Roo.form.Field} this
10206          */
10207         focus : true,
10208         /**
10209          * @event blur
10210          * Fires when this field loses input focus.
10211          * @param {Roo.form.Field} this
10212          */
10213         blur : true,
10214         /**
10215          * @event specialkey
10216          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10217          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10218          * @param {Roo.form.Field} this
10219          * @param {Roo.EventObject} e The event object
10220          */
10221         specialkey : true,
10222         /**
10223          * @event change
10224          * Fires just before the field blurs if the field value has changed.
10225          * @param {Roo.form.Field} this
10226          * @param {Mixed} newValue The new value
10227          * @param {Mixed} oldValue The original value
10228          */
10229         change : true,
10230         /**
10231          * @event invalid
10232          * Fires after the field has been marked as invalid.
10233          * @param {Roo.form.Field} this
10234          * @param {String} msg The validation message
10235          */
10236         invalid : true,
10237         /**
10238          * @event valid
10239          * Fires after the field has been validated with no errors.
10240          * @param {Roo.form.Field} this
10241          */
10242         valid : true,
10243          /**
10244          * @event keyup
10245          * Fires after the key up
10246          * @param {Roo.form.Field} this
10247          * @param {Roo.EventObject}  e The event Object
10248          */
10249         keyup : true
10250     });
10251 };
10252
10253 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10254      /**
10255      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10256       automatic validation (defaults to "keyup").
10257      */
10258     validationEvent : "keyup",
10259      /**
10260      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10261      */
10262     validateOnBlur : true,
10263     /**
10264      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10265      */
10266     validationDelay : 250,
10267      /**
10268      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10269      */
10270     focusClass : "x-form-focus",  // not needed???
10271     
10272        
10273     /**
10274      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10275      */
10276     invalidClass : "has-warning",
10277     
10278     /**
10279      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10280      */
10281     validClass : "has-success",
10282     
10283     /**
10284      * @cfg {Boolean} hasFeedback (true|false) default true
10285      */
10286     hasFeedback : true,
10287     
10288     /**
10289      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10290      */
10291     invalidFeedbackClass : "glyphicon-warning-sign",
10292     
10293     /**
10294      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10295      */
10296     validFeedbackClass : "glyphicon-ok",
10297     
10298     /**
10299      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10300      */
10301     selectOnFocus : false,
10302     
10303      /**
10304      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10305      */
10306     maskRe : null,
10307        /**
10308      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10309      */
10310     vtype : null,
10311     
10312       /**
10313      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10314      */
10315     disableKeyFilter : false,
10316     
10317        /**
10318      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10319      */
10320     disabled : false,
10321      /**
10322      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10323      */
10324     allowBlank : true,
10325     /**
10326      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10327      */
10328     blankText : "Please complete this mandatory field",
10329     
10330      /**
10331      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10332      */
10333     minLength : 0,
10334     /**
10335      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10336      */
10337     maxLength : Number.MAX_VALUE,
10338     /**
10339      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10340      */
10341     minLengthText : "The minimum length for this field is {0}",
10342     /**
10343      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10344      */
10345     maxLengthText : "The maximum length for this field is {0}",
10346   
10347     
10348     /**
10349      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10350      * If available, this function will be called only after the basic validators all return true, and will be passed the
10351      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10352      */
10353     validator : null,
10354     /**
10355      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10356      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10357      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10358      */
10359     regex : null,
10360     /**
10361      * @cfg {String} regexText -- Depricated - use Invalid Text
10362      */
10363     regexText : "",
10364     
10365     /**
10366      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10367      */
10368     invalidText : "",
10369     
10370     
10371     
10372     autocomplete: false,
10373     
10374     
10375     fieldLabel : '',
10376     inputType : 'text',
10377     
10378     name : false,
10379     placeholder: false,
10380     before : false,
10381     after : false,
10382     size : false,
10383     hasFocus : false,
10384     preventMark: false,
10385     isFormField : true,
10386     value : '',
10387     labelWidth : 2,
10388     labelAlign : false,
10389     readOnly : false,
10390     align : false,
10391     formatedValue : false,
10392     forceFeedback : false,
10393     
10394     indicatorpos : 'left',
10395     
10396     labellg : 0,
10397     labelmd : 0,
10398     labelsm : 0,
10399     labelxs : 0,
10400     
10401     capture : '',
10402     accept : '',
10403     
10404     parentLabelAlign : function()
10405     {
10406         var parent = this;
10407         while (parent.parent()) {
10408             parent = parent.parent();
10409             if (typeof(parent.labelAlign) !='undefined') {
10410                 return parent.labelAlign;
10411             }
10412         }
10413         return 'left';
10414         
10415     },
10416     
10417     getAutoCreate : function()
10418     {
10419         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10420         
10421         var id = Roo.id();
10422         
10423         var cfg = {};
10424         
10425         if(this.inputType != 'hidden'){
10426             cfg.cls = 'form-group' //input-group
10427         }
10428         
10429         var input =  {
10430             tag: 'input',
10431             id : id,
10432             type : this.inputType,
10433             value : this.value,
10434             cls : 'form-control',
10435             placeholder : this.placeholder || '',
10436             autocomplete : this.autocomplete || 'new-password'
10437         };
10438         
10439         if(this.capture.length){
10440             input.capture = this.capture;
10441         }
10442         
10443         if(this.accept.length){
10444             input.accept = this.accept + "/*";
10445         }
10446         
10447         if(this.align){
10448             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10449         }
10450         
10451         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10452             input.maxLength = this.maxLength;
10453         }
10454         
10455         if (this.disabled) {
10456             input.disabled=true;
10457         }
10458         
10459         if (this.readOnly) {
10460             input.readonly=true;
10461         }
10462         
10463         if (this.name) {
10464             input.name = this.name;
10465         }
10466         
10467         if (this.size) {
10468             input.cls += ' input-' + this.size;
10469         }
10470         
10471         var settings=this;
10472         ['xs','sm','md','lg'].map(function(size){
10473             if (settings[size]) {
10474                 cfg.cls += ' col-' + size + '-' + settings[size];
10475             }
10476         });
10477         
10478         var inputblock = input;
10479         
10480         var feedback = {
10481             tag: 'span',
10482             cls: 'glyphicon form-control-feedback'
10483         };
10484             
10485         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10486             
10487             inputblock = {
10488                 cls : 'has-feedback',
10489                 cn :  [
10490                     input,
10491                     feedback
10492                 ] 
10493             };  
10494         }
10495         
10496         if (this.before || this.after) {
10497             
10498             inputblock = {
10499                 cls : 'input-group',
10500                 cn :  [] 
10501             };
10502             
10503             if (this.before && typeof(this.before) == 'string') {
10504                 
10505                 inputblock.cn.push({
10506                     tag :'span',
10507                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10508                     html : this.before
10509                 });
10510             }
10511             if (this.before && typeof(this.before) == 'object') {
10512                 this.before = Roo.factory(this.before);
10513                 
10514                 inputblock.cn.push({
10515                     tag :'span',
10516                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
10517                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10518                 });
10519             }
10520             
10521             inputblock.cn.push(input);
10522             
10523             if (this.after && typeof(this.after) == 'string') {
10524                 inputblock.cn.push({
10525                     tag :'span',
10526                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10527                     html : this.after
10528                 });
10529             }
10530             if (this.after && typeof(this.after) == 'object') {
10531                 this.after = Roo.factory(this.after);
10532                 
10533                 inputblock.cn.push({
10534                     tag :'span',
10535                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
10536                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10537                 });
10538             }
10539             
10540             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10541                 inputblock.cls += ' has-feedback';
10542                 inputblock.cn.push(feedback);
10543             }
10544         };
10545         var indicator = {
10546             tag : 'i',
10547             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10548             tooltip : 'This field is required'
10549         };
10550         if (Roo.bootstrap.version == 4) {
10551             indicator = {
10552                 tag : 'i',
10553                 style : 'display-none'
10554             };
10555         }
10556         if (align ==='left' && this.fieldLabel.length) {
10557             
10558             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10559             
10560             cfg.cn = [
10561                 indicator,
10562                 {
10563                     tag: 'label',
10564                     'for' :  id,
10565                     cls : 'control-label col-form-label',
10566                     html : this.fieldLabel
10567
10568                 },
10569                 {
10570                     cls : "", 
10571                     cn: [
10572                         inputblock
10573                     ]
10574                 }
10575             ];
10576             
10577             var labelCfg = cfg.cn[1];
10578             var contentCfg = cfg.cn[2];
10579             
10580             if(this.indicatorpos == 'right'){
10581                 cfg.cn = [
10582                     {
10583                         tag: 'label',
10584                         'for' :  id,
10585                         cls : 'control-label col-form-label',
10586                         cn : [
10587                             {
10588                                 tag : 'span',
10589                                 html : this.fieldLabel
10590                             },
10591                             indicator
10592                         ]
10593                     },
10594                     {
10595                         cls : "",
10596                         cn: [
10597                             inputblock
10598                         ]
10599                     }
10600
10601                 ];
10602                 
10603                 labelCfg = cfg.cn[0];
10604                 contentCfg = cfg.cn[1];
10605             
10606             }
10607             
10608             if(this.labelWidth > 12){
10609                 labelCfg.style = "width: " + this.labelWidth + 'px';
10610             }
10611             
10612             if(this.labelWidth < 13 && this.labelmd == 0){
10613                 this.labelmd = this.labelWidth;
10614             }
10615             
10616             if(this.labellg > 0){
10617                 labelCfg.cls += ' col-lg-' + this.labellg;
10618                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10619             }
10620             
10621             if(this.labelmd > 0){
10622                 labelCfg.cls += ' col-md-' + this.labelmd;
10623                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10624             }
10625             
10626             if(this.labelsm > 0){
10627                 labelCfg.cls += ' col-sm-' + this.labelsm;
10628                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10629             }
10630             
10631             if(this.labelxs > 0){
10632                 labelCfg.cls += ' col-xs-' + this.labelxs;
10633                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10634             }
10635             
10636             
10637         } else if ( this.fieldLabel.length) {
10638                 
10639             cfg.cn = [
10640                 {
10641                     tag : 'i',
10642                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10643                     tooltip : 'This field is required'
10644                 },
10645                 {
10646                     tag: 'label',
10647                    //cls : 'input-group-addon',
10648                     html : this.fieldLabel
10649
10650                 },
10651
10652                inputblock
10653
10654            ];
10655            
10656            if(this.indicatorpos == 'right'){
10657                 
10658                 cfg.cn = [
10659                     {
10660                         tag: 'label',
10661                        //cls : 'input-group-addon',
10662                         html : this.fieldLabel
10663
10664                     },
10665                     {
10666                         tag : 'i',
10667                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10668                         tooltip : 'This field is required'
10669                     },
10670
10671                    inputblock
10672
10673                ];
10674
10675             }
10676
10677         } else {
10678             
10679             cfg.cn = [
10680
10681                     inputblock
10682
10683             ];
10684                 
10685                 
10686         };
10687         
10688         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10689            cfg.cls += ' navbar-form';
10690         }
10691         
10692         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10693             // on BS4 we do this only if not form 
10694             cfg.cls += ' navbar-form';
10695             cfg.tag = 'li';
10696         }
10697         
10698         return cfg;
10699         
10700     },
10701     /**
10702      * return the real input element.
10703      */
10704     inputEl: function ()
10705     {
10706         return this.el.select('input.form-control',true).first();
10707     },
10708     
10709     tooltipEl : function()
10710     {
10711         return this.inputEl();
10712     },
10713     
10714     indicatorEl : function()
10715     {
10716         if (Roo.bootstrap.version == 4) {
10717             return false; // not enabled in v4 yet.
10718         }
10719         
10720         var indicator = this.el.select('i.roo-required-indicator',true).first();
10721         
10722         if(!indicator){
10723             return false;
10724         }
10725         
10726         return indicator;
10727         
10728     },
10729     
10730     setDisabled : function(v)
10731     {
10732         var i  = this.inputEl().dom;
10733         if (!v) {
10734             i.removeAttribute('disabled');
10735             return;
10736             
10737         }
10738         i.setAttribute('disabled','true');
10739     },
10740     initEvents : function()
10741     {
10742           
10743         this.inputEl().on("keydown" , this.fireKey,  this);
10744         this.inputEl().on("focus", this.onFocus,  this);
10745         this.inputEl().on("blur", this.onBlur,  this);
10746         
10747         this.inputEl().relayEvent('keyup', this);
10748         
10749         this.indicator = this.indicatorEl();
10750         
10751         if(this.indicator){
10752             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
10753         }
10754  
10755         // reference to original value for reset
10756         this.originalValue = this.getValue();
10757         //Roo.form.TextField.superclass.initEvents.call(this);
10758         if(this.validationEvent == 'keyup'){
10759             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10760             this.inputEl().on('keyup', this.filterValidation, this);
10761         }
10762         else if(this.validationEvent !== false){
10763             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10764         }
10765         
10766         if(this.selectOnFocus){
10767             this.on("focus", this.preFocus, this);
10768             
10769         }
10770         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10771             this.inputEl().on("keypress", this.filterKeys, this);
10772         } else {
10773             this.inputEl().relayEvent('keypress', this);
10774         }
10775        /* if(this.grow){
10776             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
10777             this.el.on("click", this.autoSize,  this);
10778         }
10779         */
10780         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10781             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10782         }
10783         
10784         if (typeof(this.before) == 'object') {
10785             this.before.render(this.el.select('.roo-input-before',true).first());
10786         }
10787         if (typeof(this.after) == 'object') {
10788             this.after.render(this.el.select('.roo-input-after',true).first());
10789         }
10790         
10791         this.inputEl().on('change', this.onChange, this);
10792         
10793     },
10794     filterValidation : function(e){
10795         if(!e.isNavKeyPress()){
10796             this.validationTask.delay(this.validationDelay);
10797         }
10798     },
10799      /**
10800      * Validates the field value
10801      * @return {Boolean} True if the value is valid, else false
10802      */
10803     validate : function(){
10804         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10805         if(this.disabled || this.validateValue(this.getRawValue())){
10806             this.markValid();
10807             return true;
10808         }
10809         
10810         this.markInvalid();
10811         return false;
10812     },
10813     
10814     
10815     /**
10816      * Validates a value according to the field's validation rules and marks the field as invalid
10817      * if the validation fails
10818      * @param {Mixed} value The value to validate
10819      * @return {Boolean} True if the value is valid, else false
10820      */
10821     validateValue : function(value)
10822     {
10823         if(this.getVisibilityEl().hasClass('hidden')){
10824             return true;
10825         }
10826         
10827         if(value.length < 1)  { // if it's blank
10828             if(this.allowBlank){
10829                 return true;
10830             }
10831             return false;
10832         }
10833         
10834         if(value.length < this.minLength){
10835             return false;
10836         }
10837         if(value.length > this.maxLength){
10838             return false;
10839         }
10840         if(this.vtype){
10841             var vt = Roo.form.VTypes;
10842             if(!vt[this.vtype](value, this)){
10843                 return false;
10844             }
10845         }
10846         if(typeof this.validator == "function"){
10847             var msg = this.validator(value);
10848             if(msg !== true){
10849                 return false;
10850             }
10851             if (typeof(msg) == 'string') {
10852                 this.invalidText = msg;
10853             }
10854         }
10855         
10856         if(this.regex && !this.regex.test(value)){
10857             return false;
10858         }
10859         
10860         return true;
10861     },
10862     
10863      // private
10864     fireKey : function(e){
10865         //Roo.log('field ' + e.getKey());
10866         if(e.isNavKeyPress()){
10867             this.fireEvent("specialkey", this, e);
10868         }
10869     },
10870     focus : function (selectText){
10871         if(this.rendered){
10872             this.inputEl().focus();
10873             if(selectText === true){
10874                 this.inputEl().dom.select();
10875             }
10876         }
10877         return this;
10878     } ,
10879     
10880     onFocus : function(){
10881         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10882            // this.el.addClass(this.focusClass);
10883         }
10884         if(!this.hasFocus){
10885             this.hasFocus = true;
10886             this.startValue = this.getValue();
10887             this.fireEvent("focus", this);
10888         }
10889     },
10890     
10891     beforeBlur : Roo.emptyFn,
10892
10893     
10894     // private
10895     onBlur : function(){
10896         this.beforeBlur();
10897         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10898             //this.el.removeClass(this.focusClass);
10899         }
10900         this.hasFocus = false;
10901         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
10902             this.validate();
10903         }
10904         var v = this.getValue();
10905         if(String(v) !== String(this.startValue)){
10906             this.fireEvent('change', this, v, this.startValue);
10907         }
10908         this.fireEvent("blur", this);
10909     },
10910     
10911     onChange : function(e)
10912     {
10913         var v = this.getValue();
10914         if(String(v) !== String(this.startValue)){
10915             this.fireEvent('change', this, v, this.startValue);
10916         }
10917         
10918     },
10919     
10920     /**
10921      * Resets the current field value to the originally loaded value and clears any validation messages
10922      */
10923     reset : function(){
10924         this.setValue(this.originalValue);
10925         this.validate();
10926     },
10927      /**
10928      * Returns the name of the field
10929      * @return {Mixed} name The name field
10930      */
10931     getName: function(){
10932         return this.name;
10933     },
10934      /**
10935      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
10936      * @return {Mixed} value The field value
10937      */
10938     getValue : function(){
10939         
10940         var v = this.inputEl().getValue();
10941         
10942         return v;
10943     },
10944     /**
10945      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
10946      * @return {Mixed} value The field value
10947      */
10948     getRawValue : function(){
10949         var v = this.inputEl().getValue();
10950         
10951         return v;
10952     },
10953     
10954     /**
10955      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
10956      * @param {Mixed} value The value to set
10957      */
10958     setRawValue : function(v){
10959         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10960     },
10961     
10962     selectText : function(start, end){
10963         var v = this.getRawValue();
10964         if(v.length > 0){
10965             start = start === undefined ? 0 : start;
10966             end = end === undefined ? v.length : end;
10967             var d = this.inputEl().dom;
10968             if(d.setSelectionRange){
10969                 d.setSelectionRange(start, end);
10970             }else if(d.createTextRange){
10971                 var range = d.createTextRange();
10972                 range.moveStart("character", start);
10973                 range.moveEnd("character", v.length-end);
10974                 range.select();
10975             }
10976         }
10977     },
10978     
10979     /**
10980      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
10981      * @param {Mixed} value The value to set
10982      */
10983     setValue : function(v){
10984         this.value = v;
10985         if(this.rendered){
10986             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10987             this.validate();
10988         }
10989     },
10990     
10991     /*
10992     processValue : function(value){
10993         if(this.stripCharsRe){
10994             var newValue = value.replace(this.stripCharsRe, '');
10995             if(newValue !== value){
10996                 this.setRawValue(newValue);
10997                 return newValue;
10998             }
10999         }
11000         return value;
11001     },
11002   */
11003     preFocus : function(){
11004         
11005         if(this.selectOnFocus){
11006             this.inputEl().dom.select();
11007         }
11008     },
11009     filterKeys : function(e){
11010         var k = e.getKey();
11011         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11012             return;
11013         }
11014         var c = e.getCharCode(), cc = String.fromCharCode(c);
11015         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11016             return;
11017         }
11018         if(!this.maskRe.test(cc)){
11019             e.stopEvent();
11020         }
11021     },
11022      /**
11023      * Clear any invalid styles/messages for this field
11024      */
11025     clearInvalid : function(){
11026         
11027         if(!this.el || this.preventMark){ // not rendered
11028             return;
11029         }
11030         
11031         
11032         this.el.removeClass([this.invalidClass, 'is-invalid']);
11033         
11034         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11035             
11036             var feedback = this.el.select('.form-control-feedback', true).first();
11037             
11038             if(feedback){
11039                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11040             }
11041             
11042         }
11043         
11044         if(this.indicator){
11045             this.indicator.removeClass('visible');
11046             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11047         }
11048         
11049         this.fireEvent('valid', this);
11050     },
11051     
11052      /**
11053      * Mark this field as valid
11054      */
11055     markValid : function()
11056     {
11057         if(!this.el  || this.preventMark){ // not rendered...
11058             return;
11059         }
11060         
11061         this.el.removeClass([this.invalidClass, this.validClass]);
11062         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11063
11064         var feedback = this.el.select('.form-control-feedback', true).first();
11065             
11066         if(feedback){
11067             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11068         }
11069         
11070         if(this.indicator){
11071             this.indicator.removeClass('visible');
11072             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11073         }
11074         
11075         if(this.disabled){
11076             return;
11077         }
11078         
11079            
11080         if(this.allowBlank && !this.getRawValue().length){
11081             return;
11082         }
11083         if (Roo.bootstrap.version == 3) {
11084             this.el.addClass(this.validClass);
11085         } else {
11086             this.inputEl().addClass('is-valid');
11087         }
11088
11089         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11090             
11091             var feedback = this.el.select('.form-control-feedback', true).first();
11092             
11093             if(feedback){
11094                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11095                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11096             }
11097             
11098         }
11099         
11100         this.fireEvent('valid', this);
11101     },
11102     
11103      /**
11104      * Mark this field as invalid
11105      * @param {String} msg The validation message
11106      */
11107     markInvalid : function(msg)
11108     {
11109         if(!this.el  || this.preventMark){ // not rendered
11110             return;
11111         }
11112         
11113         this.el.removeClass([this.invalidClass, this.validClass]);
11114         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11115         
11116         var feedback = this.el.select('.form-control-feedback', true).first();
11117             
11118         if(feedback){
11119             this.el.select('.form-control-feedback', true).first().removeClass(
11120                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11121         }
11122
11123         if(this.disabled){
11124             return;
11125         }
11126         
11127         if(this.allowBlank && !this.getRawValue().length){
11128             return;
11129         }
11130         
11131         if(this.indicator){
11132             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11133             this.indicator.addClass('visible');
11134         }
11135         if (Roo.bootstrap.version == 3) {
11136             this.el.addClass(this.invalidClass);
11137         } else {
11138             this.inputEl().addClass('is-invalid');
11139         }
11140         
11141         
11142         
11143         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11144             
11145             var feedback = this.el.select('.form-control-feedback', true).first();
11146             
11147             if(feedback){
11148                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11149                 
11150                 if(this.getValue().length || this.forceFeedback){
11151                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11152                 }
11153                 
11154             }
11155             
11156         }
11157         
11158         this.fireEvent('invalid', this, msg);
11159     },
11160     // private
11161     SafariOnKeyDown : function(event)
11162     {
11163         // this is a workaround for a password hang bug on chrome/ webkit.
11164         if (this.inputEl().dom.type != 'password') {
11165             return;
11166         }
11167         
11168         var isSelectAll = false;
11169         
11170         if(this.inputEl().dom.selectionEnd > 0){
11171             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11172         }
11173         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11174             event.preventDefault();
11175             this.setValue('');
11176             return;
11177         }
11178         
11179         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11180             
11181             event.preventDefault();
11182             // this is very hacky as keydown always get's upper case.
11183             //
11184             var cc = String.fromCharCode(event.getCharCode());
11185             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11186             
11187         }
11188     },
11189     adjustWidth : function(tag, w){
11190         tag = tag.toLowerCase();
11191         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11192             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11193                 if(tag == 'input'){
11194                     return w + 2;
11195                 }
11196                 if(tag == 'textarea'){
11197                     return w-2;
11198                 }
11199             }else if(Roo.isOpera){
11200                 if(tag == 'input'){
11201                     return w + 2;
11202                 }
11203                 if(tag == 'textarea'){
11204                     return w-2;
11205                 }
11206             }
11207         }
11208         return w;
11209     },
11210     
11211     setFieldLabel : function(v)
11212     {
11213         if(!this.rendered){
11214             return;
11215         }
11216         
11217         if(this.indicatorEl()){
11218             var ar = this.el.select('label > span',true);
11219             
11220             if (ar.elements.length) {
11221                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11222                 this.fieldLabel = v;
11223                 return;
11224             }
11225             
11226             var br = this.el.select('label',true);
11227             
11228             if(br.elements.length) {
11229                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11230                 this.fieldLabel = v;
11231                 return;
11232             }
11233             
11234             Roo.log('Cannot Found any of label > span || label in input');
11235             return;
11236         }
11237         
11238         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11239         this.fieldLabel = v;
11240         
11241         
11242     }
11243 });
11244
11245  
11246 /*
11247  * - LGPL
11248  *
11249  * Input
11250  * 
11251  */
11252
11253 /**
11254  * @class Roo.bootstrap.TextArea
11255  * @extends Roo.bootstrap.Input
11256  * Bootstrap TextArea class
11257  * @cfg {Number} cols Specifies the visible width of a text area
11258  * @cfg {Number} rows Specifies the visible number of lines in a text area
11259  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11260  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11261  * @cfg {string} html text
11262  * 
11263  * @constructor
11264  * Create a new TextArea
11265  * @param {Object} config The config object
11266  */
11267
11268 Roo.bootstrap.TextArea = function(config){
11269     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11270    
11271 };
11272
11273 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11274      
11275     cols : false,
11276     rows : 5,
11277     readOnly : false,
11278     warp : 'soft',
11279     resize : false,
11280     value: false,
11281     html: false,
11282     
11283     getAutoCreate : function(){
11284         
11285         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11286         
11287         var id = Roo.id();
11288         
11289         var cfg = {};
11290         
11291         if(this.inputType != 'hidden'){
11292             cfg.cls = 'form-group' //input-group
11293         }
11294         
11295         var input =  {
11296             tag: 'textarea',
11297             id : id,
11298             warp : this.warp,
11299             rows : this.rows,
11300             value : this.value || '',
11301             html: this.html || '',
11302             cls : 'form-control',
11303             placeholder : this.placeholder || '' 
11304             
11305         };
11306         
11307         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11308             input.maxLength = this.maxLength;
11309         }
11310         
11311         if(this.resize){
11312             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11313         }
11314         
11315         if(this.cols){
11316             input.cols = this.cols;
11317         }
11318         
11319         if (this.readOnly) {
11320             input.readonly = true;
11321         }
11322         
11323         if (this.name) {
11324             input.name = this.name;
11325         }
11326         
11327         if (this.size) {
11328             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11329         }
11330         
11331         var settings=this;
11332         ['xs','sm','md','lg'].map(function(size){
11333             if (settings[size]) {
11334                 cfg.cls += ' col-' + size + '-' + settings[size];
11335             }
11336         });
11337         
11338         var inputblock = input;
11339         
11340         if(this.hasFeedback && !this.allowBlank){
11341             
11342             var feedback = {
11343                 tag: 'span',
11344                 cls: 'glyphicon form-control-feedback'
11345             };
11346
11347             inputblock = {
11348                 cls : 'has-feedback',
11349                 cn :  [
11350                     input,
11351                     feedback
11352                 ] 
11353             };  
11354         }
11355         
11356         
11357         if (this.before || this.after) {
11358             
11359             inputblock = {
11360                 cls : 'input-group',
11361                 cn :  [] 
11362             };
11363             if (this.before) {
11364                 inputblock.cn.push({
11365                     tag :'span',
11366                     cls : 'input-group-addon',
11367                     html : this.before
11368                 });
11369             }
11370             
11371             inputblock.cn.push(input);
11372             
11373             if(this.hasFeedback && !this.allowBlank){
11374                 inputblock.cls += ' has-feedback';
11375                 inputblock.cn.push(feedback);
11376             }
11377             
11378             if (this.after) {
11379                 inputblock.cn.push({
11380                     tag :'span',
11381                     cls : 'input-group-addon',
11382                     html : this.after
11383                 });
11384             }
11385             
11386         }
11387         
11388         if (align ==='left' && this.fieldLabel.length) {
11389             cfg.cn = [
11390                 {
11391                     tag: 'label',
11392                     'for' :  id,
11393                     cls : 'control-label',
11394                     html : this.fieldLabel
11395                 },
11396                 {
11397                     cls : "",
11398                     cn: [
11399                         inputblock
11400                     ]
11401                 }
11402
11403             ];
11404             
11405             if(this.labelWidth > 12){
11406                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11407             }
11408
11409             if(this.labelWidth < 13 && this.labelmd == 0){
11410                 this.labelmd = this.labelWidth;
11411             }
11412
11413             if(this.labellg > 0){
11414                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11415                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11416             }
11417
11418             if(this.labelmd > 0){
11419                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11420                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11421             }
11422
11423             if(this.labelsm > 0){
11424                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11425                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11426             }
11427
11428             if(this.labelxs > 0){
11429                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11430                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11431             }
11432             
11433         } else if ( this.fieldLabel.length) {
11434             cfg.cn = [
11435
11436                {
11437                    tag: 'label',
11438                    //cls : 'input-group-addon',
11439                    html : this.fieldLabel
11440
11441                },
11442
11443                inputblock
11444
11445            ];
11446
11447         } else {
11448
11449             cfg.cn = [
11450
11451                 inputblock
11452
11453             ];
11454                 
11455         }
11456         
11457         if (this.disabled) {
11458             input.disabled=true;
11459         }
11460         
11461         return cfg;
11462         
11463     },
11464     /**
11465      * return the real textarea element.
11466      */
11467     inputEl: function ()
11468     {
11469         return this.el.select('textarea.form-control',true).first();
11470     },
11471     
11472     /**
11473      * Clear any invalid styles/messages for this field
11474      */
11475     clearInvalid : function()
11476     {
11477         
11478         if(!this.el || this.preventMark){ // not rendered
11479             return;
11480         }
11481         
11482         var label = this.el.select('label', true).first();
11483         var icon = this.el.select('i.fa-star', true).first();
11484         
11485         if(label && icon){
11486             icon.remove();
11487         }
11488         this.el.removeClass( this.validClass);
11489         this.inputEl().removeClass('is-invalid');
11490          
11491         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11492             
11493             var feedback = this.el.select('.form-control-feedback', true).first();
11494             
11495             if(feedback){
11496                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11497             }
11498             
11499         }
11500         
11501         this.fireEvent('valid', this);
11502     },
11503     
11504      /**
11505      * Mark this field as valid
11506      */
11507     markValid : function()
11508     {
11509         if(!this.el  || this.preventMark){ // not rendered
11510             return;
11511         }
11512         
11513         this.el.removeClass([this.invalidClass, this.validClass]);
11514         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11515         
11516         var feedback = this.el.select('.form-control-feedback', true).first();
11517             
11518         if(feedback){
11519             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11520         }
11521
11522         if(this.disabled || this.allowBlank){
11523             return;
11524         }
11525         
11526         var label = this.el.select('label', true).first();
11527         var icon = this.el.select('i.fa-star', true).first();
11528         
11529         if(label && icon){
11530             icon.remove();
11531         }
11532         if (Roo.bootstrap.version == 3) {
11533             this.el.addClass(this.validClass);
11534         } else {
11535             this.inputEl().addClass('is-valid');
11536         }
11537         
11538         
11539         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11540             
11541             var feedback = this.el.select('.form-control-feedback', true).first();
11542             
11543             if(feedback){
11544                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11545                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11546             }
11547             
11548         }
11549         
11550         this.fireEvent('valid', this);
11551     },
11552     
11553      /**
11554      * Mark this field as invalid
11555      * @param {String} msg The validation message
11556      */
11557     markInvalid : function(msg)
11558     {
11559         if(!this.el  || this.preventMark){ // not rendered
11560             return;
11561         }
11562         
11563         this.el.removeClass([this.invalidClass, this.validClass]);
11564         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11565         
11566         var feedback = this.el.select('.form-control-feedback', true).first();
11567             
11568         if(feedback){
11569             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11570         }
11571
11572         if(this.disabled || this.allowBlank){
11573             return;
11574         }
11575         
11576         var label = this.el.select('label', true).first();
11577         var icon = this.el.select('i.fa-star', true).first();
11578         
11579         if(!this.getValue().length && label && !icon){
11580             this.el.createChild({
11581                 tag : 'i',
11582                 cls : 'text-danger fa fa-lg fa-star',
11583                 tooltip : 'This field is required',
11584                 style : 'margin-right:5px;'
11585             }, label, true);
11586         }
11587         
11588         if (Roo.bootstrap.version == 3) {
11589             this.el.addClass(this.invalidClass);
11590         } else {
11591             this.inputEl().addClass('is-invalid');
11592         }
11593         
11594         // fixme ... this may be depricated need to test..
11595         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11596             
11597             var feedback = this.el.select('.form-control-feedback', true).first();
11598             
11599             if(feedback){
11600                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11601                 
11602                 if(this.getValue().length || this.forceFeedback){
11603                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11604                 }
11605                 
11606             }
11607             
11608         }
11609         
11610         this.fireEvent('invalid', this, msg);
11611     }
11612 });
11613
11614  
11615 /*
11616  * - LGPL
11617  *
11618  * trigger field - base class for combo..
11619  * 
11620  */
11621  
11622 /**
11623  * @class Roo.bootstrap.TriggerField
11624  * @extends Roo.bootstrap.Input
11625  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11626  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11627  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11628  * for which you can provide a custom implementation.  For example:
11629  * <pre><code>
11630 var trigger = new Roo.bootstrap.TriggerField();
11631 trigger.onTriggerClick = myTriggerFn;
11632 trigger.applyTo('my-field');
11633 </code></pre>
11634  *
11635  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11636  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11637  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11638  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11639  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11640
11641  * @constructor
11642  * Create a new TriggerField.
11643  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11644  * to the base TextField)
11645  */
11646 Roo.bootstrap.TriggerField = function(config){
11647     this.mimicing = false;
11648     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11649 };
11650
11651 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11652     /**
11653      * @cfg {String} triggerClass A CSS class to apply to the trigger
11654      */
11655      /**
11656      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11657      */
11658     hideTrigger:false,
11659
11660     /**
11661      * @cfg {Boolean} removable (true|false) special filter default false
11662      */
11663     removable : false,
11664     
11665     /** @cfg {Boolean} grow @hide */
11666     /** @cfg {Number} growMin @hide */
11667     /** @cfg {Number} growMax @hide */
11668
11669     /**
11670      * @hide 
11671      * @method
11672      */
11673     autoSize: Roo.emptyFn,
11674     // private
11675     monitorTab : true,
11676     // private
11677     deferHeight : true,
11678
11679     
11680     actionMode : 'wrap',
11681     
11682     caret : false,
11683     
11684     
11685     getAutoCreate : function(){
11686        
11687         var align = this.labelAlign || this.parentLabelAlign();
11688         
11689         var id = Roo.id();
11690         
11691         var cfg = {
11692             cls: 'form-group' //input-group
11693         };
11694         
11695         
11696         var input =  {
11697             tag: 'input',
11698             id : id,
11699             type : this.inputType,
11700             cls : 'form-control',
11701             autocomplete: 'new-password',
11702             placeholder : this.placeholder || '' 
11703             
11704         };
11705         if (this.name) {
11706             input.name = this.name;
11707         }
11708         if (this.size) {
11709             input.cls += ' input-' + this.size;
11710         }
11711         
11712         if (this.disabled) {
11713             input.disabled=true;
11714         }
11715         
11716         var inputblock = input;
11717         
11718         if(this.hasFeedback && !this.allowBlank){
11719             
11720             var feedback = {
11721                 tag: 'span',
11722                 cls: 'glyphicon form-control-feedback'
11723             };
11724             
11725             if(this.removable && !this.editable  ){
11726                 inputblock = {
11727                     cls : 'has-feedback',
11728                     cn :  [
11729                         inputblock,
11730                         {
11731                             tag: 'button',
11732                             html : 'x',
11733                             cls : 'roo-combo-removable-btn close'
11734                         },
11735                         feedback
11736                     ] 
11737                 };
11738             } else {
11739                 inputblock = {
11740                     cls : 'has-feedback',
11741                     cn :  [
11742                         inputblock,
11743                         feedback
11744                     ] 
11745                 };
11746             }
11747
11748         } else {
11749             if(this.removable && !this.editable ){
11750                 inputblock = {
11751                     cls : 'roo-removable',
11752                     cn :  [
11753                         inputblock,
11754                         {
11755                             tag: 'button',
11756                             html : 'x',
11757                             cls : 'roo-combo-removable-btn close'
11758                         }
11759                     ] 
11760                 };
11761             }
11762         }
11763         
11764         if (this.before || this.after) {
11765             
11766             inputblock = {
11767                 cls : 'input-group',
11768                 cn :  [] 
11769             };
11770             if (this.before) {
11771                 inputblock.cn.push({
11772                     tag :'span',
11773                     cls : 'input-group-addon input-group-prepend input-group-text',
11774                     html : this.before
11775                 });
11776             }
11777             
11778             inputblock.cn.push(input);
11779             
11780             if(this.hasFeedback && !this.allowBlank){
11781                 inputblock.cls += ' has-feedback';
11782                 inputblock.cn.push(feedback);
11783             }
11784             
11785             if (this.after) {
11786                 inputblock.cn.push({
11787                     tag :'span',
11788                     cls : 'input-group-addon input-group-append input-group-text',
11789                     html : this.after
11790                 });
11791             }
11792             
11793         };
11794         
11795       
11796         
11797         var ibwrap = inputblock;
11798         
11799         if(this.multiple){
11800             ibwrap = {
11801                 tag: 'ul',
11802                 cls: 'roo-select2-choices',
11803                 cn:[
11804                     {
11805                         tag: 'li',
11806                         cls: 'roo-select2-search-field',
11807                         cn: [
11808
11809                             inputblock
11810                         ]
11811                     }
11812                 ]
11813             };
11814                 
11815         }
11816         
11817         var combobox = {
11818             cls: 'roo-select2-container input-group',
11819             cn: [
11820                  {
11821                     tag: 'input',
11822                     type : 'hidden',
11823                     cls: 'form-hidden-field'
11824                 },
11825                 ibwrap
11826             ]
11827         };
11828         
11829         if(!this.multiple && this.showToggleBtn){
11830             
11831             var caret = {
11832                         tag: 'span',
11833                         cls: 'caret'
11834              };
11835             if (this.caret != false) {
11836                 caret = {
11837                      tag: 'i',
11838                      cls: 'fa fa-' + this.caret
11839                 };
11840                 
11841             }
11842             
11843             combobox.cn.push({
11844                 tag :'span',
11845                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11846                 cn : [
11847                     Roo.bootstrap.version == 3 ? caret : '',
11848                     {
11849                         tag: 'span',
11850                         cls: 'combobox-clear',
11851                         cn  : [
11852                             {
11853                                 tag : 'i',
11854                                 cls: 'icon-remove'
11855                             }
11856                         ]
11857                     }
11858                 ]
11859
11860             })
11861         }
11862         
11863         if(this.multiple){
11864             combobox.cls += ' roo-select2-container-multi';
11865         }
11866          var indicator = {
11867             tag : 'i',
11868             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11869             tooltip : 'This field is required'
11870         };
11871         if (Roo.bootstrap.version == 4) {
11872             indicator = {
11873                 tag : 'i',
11874                 style : 'display:none'
11875             };
11876         }
11877         
11878         
11879         if (align ==='left' && this.fieldLabel.length) {
11880             
11881             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11882
11883             cfg.cn = [
11884                 indicator,
11885                 {
11886                     tag: 'label',
11887                     'for' :  id,
11888                     cls : 'control-label',
11889                     html : this.fieldLabel
11890
11891                 },
11892                 {
11893                     cls : "", 
11894                     cn: [
11895                         combobox
11896                     ]
11897                 }
11898
11899             ];
11900             
11901             var labelCfg = cfg.cn[1];
11902             var contentCfg = cfg.cn[2];
11903             
11904             if(this.indicatorpos == 'right'){
11905                 cfg.cn = [
11906                     {
11907                         tag: 'label',
11908                         'for' :  id,
11909                         cls : 'control-label',
11910                         cn : [
11911                             {
11912                                 tag : 'span',
11913                                 html : this.fieldLabel
11914                             },
11915                             indicator
11916                         ]
11917                     },
11918                     {
11919                         cls : "", 
11920                         cn: [
11921                             combobox
11922                         ]
11923                     }
11924
11925                 ];
11926                 
11927                 labelCfg = cfg.cn[0];
11928                 contentCfg = cfg.cn[1];
11929             }
11930             
11931             if(this.labelWidth > 12){
11932                 labelCfg.style = "width: " + this.labelWidth + 'px';
11933             }
11934             
11935             if(this.labelWidth < 13 && this.labelmd == 0){
11936                 this.labelmd = this.labelWidth;
11937             }
11938             
11939             if(this.labellg > 0){
11940                 labelCfg.cls += ' col-lg-' + this.labellg;
11941                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11942             }
11943             
11944             if(this.labelmd > 0){
11945                 labelCfg.cls += ' col-md-' + this.labelmd;
11946                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11947             }
11948             
11949             if(this.labelsm > 0){
11950                 labelCfg.cls += ' col-sm-' + this.labelsm;
11951                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11952             }
11953             
11954             if(this.labelxs > 0){
11955                 labelCfg.cls += ' col-xs-' + this.labelxs;
11956                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11957             }
11958             
11959         } else if ( this.fieldLabel.length) {
11960 //                Roo.log(" label");
11961             cfg.cn = [
11962                 indicator,
11963                {
11964                    tag: 'label',
11965                    //cls : 'input-group-addon',
11966                    html : this.fieldLabel
11967
11968                },
11969
11970                combobox
11971
11972             ];
11973             
11974             if(this.indicatorpos == 'right'){
11975                 
11976                 cfg.cn = [
11977                     {
11978                        tag: 'label',
11979                        cn : [
11980                            {
11981                                tag : 'span',
11982                                html : this.fieldLabel
11983                            },
11984                            indicator
11985                        ]
11986
11987                     },
11988                     combobox
11989
11990                 ];
11991
11992             }
11993
11994         } else {
11995             
11996 //                Roo.log(" no label && no align");
11997                 cfg = combobox
11998                      
11999                 
12000         }
12001         
12002         var settings=this;
12003         ['xs','sm','md','lg'].map(function(size){
12004             if (settings[size]) {
12005                 cfg.cls += ' col-' + size + '-' + settings[size];
12006             }
12007         });
12008         
12009         return cfg;
12010         
12011     },
12012     
12013     
12014     
12015     // private
12016     onResize : function(w, h){
12017 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12018 //        if(typeof w == 'number'){
12019 //            var x = w - this.trigger.getWidth();
12020 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12021 //            this.trigger.setStyle('left', x+'px');
12022 //        }
12023     },
12024
12025     // private
12026     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12027
12028     // private
12029     getResizeEl : function(){
12030         return this.inputEl();
12031     },
12032
12033     // private
12034     getPositionEl : function(){
12035         return this.inputEl();
12036     },
12037
12038     // private
12039     alignErrorIcon : function(){
12040         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12041     },
12042
12043     // private
12044     initEvents : function(){
12045         
12046         this.createList();
12047         
12048         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12049         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12050         if(!this.multiple && this.showToggleBtn){
12051             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12052             if(this.hideTrigger){
12053                 this.trigger.setDisplayed(false);
12054             }
12055             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12056         }
12057         
12058         if(this.multiple){
12059             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12060         }
12061         
12062         if(this.removable && !this.editable && !this.tickable){
12063             var close = this.closeTriggerEl();
12064             
12065             if(close){
12066                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12067                 close.on('click', this.removeBtnClick, this, close);
12068             }
12069         }
12070         
12071         //this.trigger.addClassOnOver('x-form-trigger-over');
12072         //this.trigger.addClassOnClick('x-form-trigger-click');
12073         
12074         //if(!this.width){
12075         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12076         //}
12077     },
12078     
12079     closeTriggerEl : function()
12080     {
12081         var close = this.el.select('.roo-combo-removable-btn', true).first();
12082         return close ? close : false;
12083     },
12084     
12085     removeBtnClick : function(e, h, el)
12086     {
12087         e.preventDefault();
12088         
12089         if(this.fireEvent("remove", this) !== false){
12090             this.reset();
12091             this.fireEvent("afterremove", this)
12092         }
12093     },
12094     
12095     createList : function()
12096     {
12097         this.list = Roo.get(document.body).createChild({
12098             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12099             cls: 'typeahead typeahead-long dropdown-menu',
12100             style: 'display:none'
12101         });
12102         
12103         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12104         
12105     },
12106
12107     // private
12108     initTrigger : function(){
12109        
12110     },
12111
12112     // private
12113     onDestroy : function(){
12114         if(this.trigger){
12115             this.trigger.removeAllListeners();
12116           //  this.trigger.remove();
12117         }
12118         //if(this.wrap){
12119         //    this.wrap.remove();
12120         //}
12121         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12122     },
12123
12124     // private
12125     onFocus : function(){
12126         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12127         /*
12128         if(!this.mimicing){
12129             this.wrap.addClass('x-trigger-wrap-focus');
12130             this.mimicing = true;
12131             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12132             if(this.monitorTab){
12133                 this.el.on("keydown", this.checkTab, this);
12134             }
12135         }
12136         */
12137     },
12138
12139     // private
12140     checkTab : function(e){
12141         if(e.getKey() == e.TAB){
12142             this.triggerBlur();
12143         }
12144     },
12145
12146     // private
12147     onBlur : function(){
12148         // do nothing
12149     },
12150
12151     // private
12152     mimicBlur : function(e, t){
12153         /*
12154         if(!this.wrap.contains(t) && this.validateBlur()){
12155             this.triggerBlur();
12156         }
12157         */
12158     },
12159
12160     // private
12161     triggerBlur : function(){
12162         this.mimicing = false;
12163         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12164         if(this.monitorTab){
12165             this.el.un("keydown", this.checkTab, this);
12166         }
12167         //this.wrap.removeClass('x-trigger-wrap-focus');
12168         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12169     },
12170
12171     // private
12172     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12173     validateBlur : function(e, t){
12174         return true;
12175     },
12176
12177     // private
12178     onDisable : function(){
12179         this.inputEl().dom.disabled = true;
12180         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12181         //if(this.wrap){
12182         //    this.wrap.addClass('x-item-disabled');
12183         //}
12184     },
12185
12186     // private
12187     onEnable : function(){
12188         this.inputEl().dom.disabled = false;
12189         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12190         //if(this.wrap){
12191         //    this.el.removeClass('x-item-disabled');
12192         //}
12193     },
12194
12195     // private
12196     onShow : function(){
12197         var ae = this.getActionEl();
12198         
12199         if(ae){
12200             ae.dom.style.display = '';
12201             ae.dom.style.visibility = 'visible';
12202         }
12203     },
12204
12205     // private
12206     
12207     onHide : function(){
12208         var ae = this.getActionEl();
12209         ae.dom.style.display = 'none';
12210     },
12211
12212     /**
12213      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12214      * by an implementing function.
12215      * @method
12216      * @param {EventObject} e
12217      */
12218     onTriggerClick : Roo.emptyFn
12219 });
12220  
12221 /*
12222 * Licence: LGPL
12223 */
12224
12225 /**
12226  * @class Roo.bootstrap.CardUploader
12227  * @extends Roo.bootstrap.Button
12228  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12229  * @cfg {Number} errorTimeout default 3000
12230  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12231  * @cfg {Array}  html The button text.
12232
12233  *
12234  * @constructor
12235  * Create a new CardUploader
12236  * @param {Object} config The config object
12237  */
12238
12239 Roo.bootstrap.CardUploader = function(config){
12240     
12241  
12242     
12243     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12244     
12245     
12246     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12247         return r.data.id
12248         });
12249     
12250     
12251 };
12252
12253 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12254     
12255      
12256     errorTimeout : 3000,
12257      
12258     images : false,
12259    
12260     fileCollection : false,
12261     allowBlank : true,
12262     
12263     getAutoCreate : function()
12264     {
12265         
12266         var cfg =  {
12267             cls :'form-group' ,
12268             cn : [
12269                
12270                 {
12271                     tag: 'label',
12272                    //cls : 'input-group-addon',
12273                     html : this.fieldLabel
12274
12275                 },
12276
12277                 {
12278                     tag: 'input',
12279                     type : 'hidden',
12280                     value : this.value,
12281                     cls : 'd-none  form-control'
12282                 },
12283                 
12284                 {
12285                     tag: 'input',
12286                     multiple : 'multiple',
12287                     type : 'file',
12288                     cls : 'd-none  roo-card-upload-selector'
12289                 },
12290                 
12291                 {
12292                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12293                 },
12294                 {
12295                     cls : 'card-columns roo-card-uploader-container'
12296                 }
12297
12298             ]
12299         };
12300            
12301          
12302         return cfg;
12303     },
12304     
12305     getChildContainer : function() /// what children are added to.
12306     {
12307         return this.containerEl;
12308     },
12309    
12310     getButtonContainer : function() /// what children are added to.
12311     {
12312         return this.el.select(".roo-card-uploader-button-container").first();
12313     },
12314    
12315     initEvents : function()
12316     {
12317         
12318         Roo.bootstrap.Input.prototype.initEvents.call(this);
12319         
12320         var t = this;
12321         this.addxtype({
12322             xns: Roo.bootstrap,
12323
12324             xtype : 'Button',
12325             container_method : 'getButtonContainer' ,            
12326             html :  this.html, // fix changable?
12327             cls : 'w-100 ',
12328             listeners : {
12329                 'click' : function(btn, e) {
12330                     t.onClick(e);
12331                 }
12332             }
12333         });
12334         
12335         
12336         
12337         
12338         this.urlAPI = (window.createObjectURL && window) || 
12339                                 (window.URL && URL.revokeObjectURL && URL) || 
12340                                 (window.webkitURL && webkitURL);
12341                         
12342          
12343          
12344          
12345         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12346         
12347         this.selectorEl.on('change', this.onFileSelected, this);
12348         if (this.images) {
12349             var t = this;
12350             this.images.forEach(function(img) {
12351                 t.addCard(img)
12352             });
12353             this.images = false;
12354         }
12355         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12356          
12357        
12358     },
12359     
12360    
12361     onClick : function(e)
12362     {
12363         e.preventDefault();
12364          
12365         this.selectorEl.dom.click();
12366          
12367     },
12368     
12369     onFileSelected : function(e)
12370     {
12371         e.preventDefault();
12372         
12373         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12374             return;
12375         }
12376         
12377         Roo.each(this.selectorEl.dom.files, function(file){    
12378             this.addFile(file);
12379         }, this);
12380          
12381     },
12382     
12383       
12384     
12385       
12386     
12387     addFile : function(file)
12388     {
12389            
12390         if(typeof(file) === 'string'){
12391             throw "Add file by name?"; // should not happen
12392             return;
12393         }
12394         
12395         if(!file || !this.urlAPI){
12396             return;
12397         }
12398         
12399         // file;
12400         // file.type;
12401         
12402         var _this = this;
12403         
12404         
12405         var url = _this.urlAPI.createObjectURL( file);
12406            
12407         this.addCard({
12408             id : Roo.bootstrap.CardUploader.ID--,
12409             is_uploaded : false,
12410             src : url,
12411             title : file.name,
12412             mimetype : file.type,
12413             preview : false,
12414             is_deleted : 0
12415         })
12416         
12417     },
12418     
12419     addCard : function (data)
12420     {
12421         // hidden input element?
12422         // if the file is not an image...
12423         //then we need to use something other that and header_image
12424         var t = this;
12425         //   remove.....
12426         var footer = [
12427             {
12428                 xns : Roo.bootstrap,
12429                 xtype : 'CardFooter',
12430                 items: [
12431                     {
12432                         xns : Roo.bootstrap,
12433                         xtype : 'Element',
12434                         cls : 'd-flex',
12435                         items : [
12436                             
12437                             {
12438                                 xns : Roo.bootstrap,
12439                                 xtype : 'Button',
12440                                 html : String.format("<small>{0}</small>", data.title),
12441                                 cls : 'col-11 text-left',
12442                                 size: 'sm',
12443                                 weight: 'link',
12444                                 fa : 'download',
12445                                 listeners : {
12446                                     click : function() {
12447                                         this.downloadCard(data.id)
12448                                     }
12449                                 }
12450                             },
12451                           
12452                             {
12453                                 xns : Roo.bootstrap,
12454                                 xtype : 'Button',
12455                                 
12456                                 size : 'sm',
12457                                 weight: 'danger',
12458                                 cls : 'col-1',
12459                                 fa : 'times',
12460                                 listeners : {
12461                                     click : function() {
12462                                         t.removeCard(data.id)
12463                                     }
12464                                 }
12465                             }
12466                         ]
12467                     }
12468                     
12469                 ] 
12470             }
12471             
12472         ];
12473
12474         var cn = this.addxtype(
12475             {
12476                  
12477                 xns : Roo.bootstrap,
12478                 xtype : 'Card',
12479                 closeable : true,
12480                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12481                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12482                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12483                 data : data,
12484                 html : false,
12485                  
12486                 items : footer,
12487                 initEvents : function() {
12488                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12489                     this.imgEl = this.el.select('.card-img-top').first();
12490                     if (this.imgEl) {
12491                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12492                         this.imgEl.set({ 'pointer' : 'cursor' });
12493                                   
12494                     }
12495                     
12496                   
12497                 }
12498                 
12499             }
12500         );
12501         // dont' really need ot update items.
12502         // this.items.push(cn);
12503         this.fileCollection.add(cn);
12504         this.updateInput();
12505         
12506     },
12507     removeCard : function(id)
12508     {
12509         
12510         var card  = this.fileCollection.get(id);
12511         card.data.is_deleted = 1;
12512         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12513         this.fileCollection.remove(card);
12514         //this.items = this.items.filter(function(e) { return e != card });
12515         // dont' really need ot update items.
12516         card.el.dom.parentNode.removeChild(card.el.dom);
12517         
12518     },
12519     reset: function()
12520     {
12521         this.fileCollection.each(function(card) {
12522             card.el.dom.parentNode.removeChild(card.el.dom);    
12523         });
12524         this.fileCollection.clear();
12525         this.updateInput();
12526     },
12527     
12528     updateInput : function()
12529     {
12530         var data = [];
12531         this.fileCollection.each(function(e) {
12532             data.push(e.data);
12533         });
12534         
12535         this.inputEl().dom.value = JSON.stringify(data);
12536     }
12537     
12538     
12539 });
12540
12541
12542 Roo.bootstrap.CardUploader.ID = -1;/*
12543  * Based on:
12544  * Ext JS Library 1.1.1
12545  * Copyright(c) 2006-2007, Ext JS, LLC.
12546  *
12547  * Originally Released Under LGPL - original licence link has changed is not relivant.
12548  *
12549  * Fork - LGPL
12550  * <script type="text/javascript">
12551  */
12552
12553
12554 /**
12555  * @class Roo.data.SortTypes
12556  * @singleton
12557  * Defines the default sorting (casting?) comparison functions used when sorting data.
12558  */
12559 Roo.data.SortTypes = {
12560     /**
12561      * Default sort that does nothing
12562      * @param {Mixed} s The value being converted
12563      * @return {Mixed} The comparison value
12564      */
12565     none : function(s){
12566         return s;
12567     },
12568     
12569     /**
12570      * The regular expression used to strip tags
12571      * @type {RegExp}
12572      * @property
12573      */
12574     stripTagsRE : /<\/?[^>]+>/gi,
12575     
12576     /**
12577      * Strips all HTML tags to sort on text only
12578      * @param {Mixed} s The value being converted
12579      * @return {String} The comparison value
12580      */
12581     asText : function(s){
12582         return String(s).replace(this.stripTagsRE, "");
12583     },
12584     
12585     /**
12586      * Strips all HTML tags to sort on text only - Case insensitive
12587      * @param {Mixed} s The value being converted
12588      * @return {String} The comparison value
12589      */
12590     asUCText : function(s){
12591         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12592     },
12593     
12594     /**
12595      * Case insensitive string
12596      * @param {Mixed} s The value being converted
12597      * @return {String} The comparison value
12598      */
12599     asUCString : function(s) {
12600         return String(s).toUpperCase();
12601     },
12602     
12603     /**
12604      * Date sorting
12605      * @param {Mixed} s The value being converted
12606      * @return {Number} The comparison value
12607      */
12608     asDate : function(s) {
12609         if(!s){
12610             return 0;
12611         }
12612         if(s instanceof Date){
12613             return s.getTime();
12614         }
12615         return Date.parse(String(s));
12616     },
12617     
12618     /**
12619      * Float sorting
12620      * @param {Mixed} s The value being converted
12621      * @return {Float} The comparison value
12622      */
12623     asFloat : function(s) {
12624         var val = parseFloat(String(s).replace(/,/g, ""));
12625         if(isNaN(val)) {
12626             val = 0;
12627         }
12628         return val;
12629     },
12630     
12631     /**
12632      * Integer sorting
12633      * @param {Mixed} s The value being converted
12634      * @return {Number} The comparison value
12635      */
12636     asInt : function(s) {
12637         var val = parseInt(String(s).replace(/,/g, ""));
12638         if(isNaN(val)) {
12639             val = 0;
12640         }
12641         return val;
12642     }
12643 };/*
12644  * Based on:
12645  * Ext JS Library 1.1.1
12646  * Copyright(c) 2006-2007, Ext JS, LLC.
12647  *
12648  * Originally Released Under LGPL - original licence link has changed is not relivant.
12649  *
12650  * Fork - LGPL
12651  * <script type="text/javascript">
12652  */
12653
12654 /**
12655 * @class Roo.data.Record
12656  * Instances of this class encapsulate both record <em>definition</em> information, and record
12657  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12658  * to access Records cached in an {@link Roo.data.Store} object.<br>
12659  * <p>
12660  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12661  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12662  * objects.<br>
12663  * <p>
12664  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12665  * @constructor
12666  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12667  * {@link #create}. The parameters are the same.
12668  * @param {Array} data An associative Array of data values keyed by the field name.
12669  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12670  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12671  * not specified an integer id is generated.
12672  */
12673 Roo.data.Record = function(data, id){
12674     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12675     this.data = data;
12676 };
12677
12678 /**
12679  * Generate a constructor for a specific record layout.
12680  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12681  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12682  * Each field definition object may contain the following properties: <ul>
12683  * <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,
12684  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12685  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12686  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12687  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12688  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12689  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12690  * this may be omitted.</p></li>
12691  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12692  * <ul><li>auto (Default, implies no conversion)</li>
12693  * <li>string</li>
12694  * <li>int</li>
12695  * <li>float</li>
12696  * <li>boolean</li>
12697  * <li>date</li></ul></p></li>
12698  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12699  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12700  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12701  * by the Reader into an object that will be stored in the Record. It is passed the
12702  * following parameters:<ul>
12703  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12704  * </ul></p></li>
12705  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12706  * </ul>
12707  * <br>usage:<br><pre><code>
12708 var TopicRecord = Roo.data.Record.create(
12709     {name: 'title', mapping: 'topic_title'},
12710     {name: 'author', mapping: 'username'},
12711     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12712     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12713     {name: 'lastPoster', mapping: 'user2'},
12714     {name: 'excerpt', mapping: 'post_text'}
12715 );
12716
12717 var myNewRecord = new TopicRecord({
12718     title: 'Do my job please',
12719     author: 'noobie',
12720     totalPosts: 1,
12721     lastPost: new Date(),
12722     lastPoster: 'Animal',
12723     excerpt: 'No way dude!'
12724 });
12725 myStore.add(myNewRecord);
12726 </code></pre>
12727  * @method create
12728  * @static
12729  */
12730 Roo.data.Record.create = function(o){
12731     var f = function(){
12732         f.superclass.constructor.apply(this, arguments);
12733     };
12734     Roo.extend(f, Roo.data.Record);
12735     var p = f.prototype;
12736     p.fields = new Roo.util.MixedCollection(false, function(field){
12737         return field.name;
12738     });
12739     for(var i = 0, len = o.length; i < len; i++){
12740         p.fields.add(new Roo.data.Field(o[i]));
12741     }
12742     f.getField = function(name){
12743         return p.fields.get(name);  
12744     };
12745     return f;
12746 };
12747
12748 Roo.data.Record.AUTO_ID = 1000;
12749 Roo.data.Record.EDIT = 'edit';
12750 Roo.data.Record.REJECT = 'reject';
12751 Roo.data.Record.COMMIT = 'commit';
12752
12753 Roo.data.Record.prototype = {
12754     /**
12755      * Readonly flag - true if this record has been modified.
12756      * @type Boolean
12757      */
12758     dirty : false,
12759     editing : false,
12760     error: null,
12761     modified: null,
12762
12763     // private
12764     join : function(store){
12765         this.store = store;
12766     },
12767
12768     /**
12769      * Set the named field to the specified value.
12770      * @param {String} name The name of the field to set.
12771      * @param {Object} value The value to set the field to.
12772      */
12773     set : function(name, value){
12774         if(this.data[name] == value){
12775             return;
12776         }
12777         this.dirty = true;
12778         if(!this.modified){
12779             this.modified = {};
12780         }
12781         if(typeof this.modified[name] == 'undefined'){
12782             this.modified[name] = this.data[name];
12783         }
12784         this.data[name] = value;
12785         if(!this.editing && this.store){
12786             this.store.afterEdit(this);
12787         }       
12788     },
12789
12790     /**
12791      * Get the value of the named field.
12792      * @param {String} name The name of the field to get the value of.
12793      * @return {Object} The value of the field.
12794      */
12795     get : function(name){
12796         return this.data[name]; 
12797     },
12798
12799     // private
12800     beginEdit : function(){
12801         this.editing = true;
12802         this.modified = {}; 
12803     },
12804
12805     // private
12806     cancelEdit : function(){
12807         this.editing = false;
12808         delete this.modified;
12809     },
12810
12811     // private
12812     endEdit : function(){
12813         this.editing = false;
12814         if(this.dirty && this.store){
12815             this.store.afterEdit(this);
12816         }
12817     },
12818
12819     /**
12820      * Usually called by the {@link Roo.data.Store} which owns the Record.
12821      * Rejects all changes made to the Record since either creation, or the last commit operation.
12822      * Modified fields are reverted to their original values.
12823      * <p>
12824      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12825      * of reject operations.
12826      */
12827     reject : function(){
12828         var m = this.modified;
12829         for(var n in m){
12830             if(typeof m[n] != "function"){
12831                 this.data[n] = m[n];
12832             }
12833         }
12834         this.dirty = false;
12835         delete this.modified;
12836         this.editing = false;
12837         if(this.store){
12838             this.store.afterReject(this);
12839         }
12840     },
12841
12842     /**
12843      * Usually called by the {@link Roo.data.Store} which owns the Record.
12844      * Commits all changes made to the Record since either creation, or the last commit operation.
12845      * <p>
12846      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12847      * of commit operations.
12848      */
12849     commit : function(){
12850         this.dirty = false;
12851         delete this.modified;
12852         this.editing = false;
12853         if(this.store){
12854             this.store.afterCommit(this);
12855         }
12856     },
12857
12858     // private
12859     hasError : function(){
12860         return this.error != null;
12861     },
12862
12863     // private
12864     clearError : function(){
12865         this.error = null;
12866     },
12867
12868     /**
12869      * Creates a copy of this record.
12870      * @param {String} id (optional) A new record id if you don't want to use this record's id
12871      * @return {Record}
12872      */
12873     copy : function(newId) {
12874         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12875     }
12876 };/*
12877  * Based on:
12878  * Ext JS Library 1.1.1
12879  * Copyright(c) 2006-2007, Ext JS, LLC.
12880  *
12881  * Originally Released Under LGPL - original licence link has changed is not relivant.
12882  *
12883  * Fork - LGPL
12884  * <script type="text/javascript">
12885  */
12886
12887
12888
12889 /**
12890  * @class Roo.data.Store
12891  * @extends Roo.util.Observable
12892  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12893  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12894  * <p>
12895  * 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
12896  * has no knowledge of the format of the data returned by the Proxy.<br>
12897  * <p>
12898  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
12899  * instances from the data object. These records are cached and made available through accessor functions.
12900  * @constructor
12901  * Creates a new Store.
12902  * @param {Object} config A config object containing the objects needed for the Store to access data,
12903  * and read the data into Records.
12904  */
12905 Roo.data.Store = function(config){
12906     this.data = new Roo.util.MixedCollection(false);
12907     this.data.getKey = function(o){
12908         return o.id;
12909     };
12910     this.baseParams = {};
12911     // private
12912     this.paramNames = {
12913         "start" : "start",
12914         "limit" : "limit",
12915         "sort" : "sort",
12916         "dir" : "dir",
12917         "multisort" : "_multisort"
12918     };
12919
12920     if(config && config.data){
12921         this.inlineData = config.data;
12922         delete config.data;
12923     }
12924
12925     Roo.apply(this, config);
12926     
12927     if(this.reader){ // reader passed
12928         this.reader = Roo.factory(this.reader, Roo.data);
12929         this.reader.xmodule = this.xmodule || false;
12930         if(!this.recordType){
12931             this.recordType = this.reader.recordType;
12932         }
12933         if(this.reader.onMetaChange){
12934             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
12935         }
12936     }
12937
12938     if(this.recordType){
12939         this.fields = this.recordType.prototype.fields;
12940     }
12941     this.modified = [];
12942
12943     this.addEvents({
12944         /**
12945          * @event datachanged
12946          * Fires when the data cache has changed, and a widget which is using this Store
12947          * as a Record cache should refresh its view.
12948          * @param {Store} this
12949          */
12950         datachanged : true,
12951         /**
12952          * @event metachange
12953          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
12954          * @param {Store} this
12955          * @param {Object} meta The JSON metadata
12956          */
12957         metachange : true,
12958         /**
12959          * @event add
12960          * Fires when Records have been added to the Store
12961          * @param {Store} this
12962          * @param {Roo.data.Record[]} records The array of Records added
12963          * @param {Number} index The index at which the record(s) were added
12964          */
12965         add : true,
12966         /**
12967          * @event remove
12968          * Fires when a Record has been removed from the Store
12969          * @param {Store} this
12970          * @param {Roo.data.Record} record The Record that was removed
12971          * @param {Number} index The index at which the record was removed
12972          */
12973         remove : true,
12974         /**
12975          * @event update
12976          * Fires when a Record has been updated
12977          * @param {Store} this
12978          * @param {Roo.data.Record} record The Record that was updated
12979          * @param {String} operation The update operation being performed.  Value may be one of:
12980          * <pre><code>
12981  Roo.data.Record.EDIT
12982  Roo.data.Record.REJECT
12983  Roo.data.Record.COMMIT
12984          * </code></pre>
12985          */
12986         update : true,
12987         /**
12988          * @event clear
12989          * Fires when the data cache has been cleared.
12990          * @param {Store} this
12991          */
12992         clear : true,
12993         /**
12994          * @event beforeload
12995          * Fires before a request is made for a new data object.  If the beforeload handler returns false
12996          * the load action will be canceled.
12997          * @param {Store} this
12998          * @param {Object} options The loading options that were specified (see {@link #load} for details)
12999          */
13000         beforeload : true,
13001         /**
13002          * @event beforeloadadd
13003          * Fires after a new set of Records has been loaded.
13004          * @param {Store} this
13005          * @param {Roo.data.Record[]} records The Records that were loaded
13006          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13007          */
13008         beforeloadadd : true,
13009         /**
13010          * @event load
13011          * Fires after a new set of Records has been loaded, before they are added to the store.
13012          * @param {Store} this
13013          * @param {Roo.data.Record[]} records The Records that were loaded
13014          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13015          * @params {Object} return from reader
13016          */
13017         load : true,
13018         /**
13019          * @event loadexception
13020          * Fires if an exception occurs in the Proxy during loading.
13021          * Called with the signature of the Proxy's "loadexception" event.
13022          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13023          * 
13024          * @param {Proxy} 
13025          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13026          * @param {Object} load options 
13027          * @param {Object} jsonData from your request (normally this contains the Exception)
13028          */
13029         loadexception : true
13030     });
13031     
13032     if(this.proxy){
13033         this.proxy = Roo.factory(this.proxy, Roo.data);
13034         this.proxy.xmodule = this.xmodule || false;
13035         this.relayEvents(this.proxy,  ["loadexception"]);
13036     }
13037     this.sortToggle = {};
13038     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13039
13040     Roo.data.Store.superclass.constructor.call(this);
13041
13042     if(this.inlineData){
13043         this.loadData(this.inlineData);
13044         delete this.inlineData;
13045     }
13046 };
13047
13048 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13049      /**
13050     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13051     * without a remote query - used by combo/forms at present.
13052     */
13053     
13054     /**
13055     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13056     */
13057     /**
13058     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13059     */
13060     /**
13061     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13062     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13063     */
13064     /**
13065     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13066     * on any HTTP request
13067     */
13068     /**
13069     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13070     */
13071     /**
13072     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13073     */
13074     multiSort: false,
13075     /**
13076     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13077     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13078     */
13079     remoteSort : false,
13080
13081     /**
13082     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13083      * loaded or when a record is removed. (defaults to false).
13084     */
13085     pruneModifiedRecords : false,
13086
13087     // private
13088     lastOptions : null,
13089
13090     /**
13091      * Add Records to the Store and fires the add event.
13092      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13093      */
13094     add : function(records){
13095         records = [].concat(records);
13096         for(var i = 0, len = records.length; i < len; i++){
13097             records[i].join(this);
13098         }
13099         var index = this.data.length;
13100         this.data.addAll(records);
13101         this.fireEvent("add", this, records, index);
13102     },
13103
13104     /**
13105      * Remove a Record from the Store and fires the remove event.
13106      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13107      */
13108     remove : function(record){
13109         var index = this.data.indexOf(record);
13110         this.data.removeAt(index);
13111  
13112         if(this.pruneModifiedRecords){
13113             this.modified.remove(record);
13114         }
13115         this.fireEvent("remove", this, record, index);
13116     },
13117
13118     /**
13119      * Remove all Records from the Store and fires the clear event.
13120      */
13121     removeAll : function(){
13122         this.data.clear();
13123         if(this.pruneModifiedRecords){
13124             this.modified = [];
13125         }
13126         this.fireEvent("clear", this);
13127     },
13128
13129     /**
13130      * Inserts Records to the Store at the given index and fires the add event.
13131      * @param {Number} index The start index at which to insert the passed Records.
13132      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13133      */
13134     insert : function(index, records){
13135         records = [].concat(records);
13136         for(var i = 0, len = records.length; i < len; i++){
13137             this.data.insert(index, records[i]);
13138             records[i].join(this);
13139         }
13140         this.fireEvent("add", this, records, index);
13141     },
13142
13143     /**
13144      * Get the index within the cache of the passed Record.
13145      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13146      * @return {Number} The index of the passed Record. Returns -1 if not found.
13147      */
13148     indexOf : function(record){
13149         return this.data.indexOf(record);
13150     },
13151
13152     /**
13153      * Get the index within the cache of the Record with the passed id.
13154      * @param {String} id The id of the Record to find.
13155      * @return {Number} The index of the Record. Returns -1 if not found.
13156      */
13157     indexOfId : function(id){
13158         return this.data.indexOfKey(id);
13159     },
13160
13161     /**
13162      * Get the Record with the specified id.
13163      * @param {String} id The id of the Record to find.
13164      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13165      */
13166     getById : function(id){
13167         return this.data.key(id);
13168     },
13169
13170     /**
13171      * Get the Record at the specified index.
13172      * @param {Number} index The index of the Record to find.
13173      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13174      */
13175     getAt : function(index){
13176         return this.data.itemAt(index);
13177     },
13178
13179     /**
13180      * Returns a range of Records between specified indices.
13181      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13182      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13183      * @return {Roo.data.Record[]} An array of Records
13184      */
13185     getRange : function(start, end){
13186         return this.data.getRange(start, end);
13187     },
13188
13189     // private
13190     storeOptions : function(o){
13191         o = Roo.apply({}, o);
13192         delete o.callback;
13193         delete o.scope;
13194         this.lastOptions = o;
13195     },
13196
13197     /**
13198      * Loads the Record cache from the configured Proxy using the configured Reader.
13199      * <p>
13200      * If using remote paging, then the first load call must specify the <em>start</em>
13201      * and <em>limit</em> properties in the options.params property to establish the initial
13202      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13203      * <p>
13204      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13205      * and this call will return before the new data has been loaded. Perform any post-processing
13206      * in a callback function, or in a "load" event handler.</strong>
13207      * <p>
13208      * @param {Object} options An object containing properties which control loading options:<ul>
13209      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13210      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13211      * passed the following arguments:<ul>
13212      * <li>r : Roo.data.Record[]</li>
13213      * <li>options: Options object from the load call</li>
13214      * <li>success: Boolean success indicator</li></ul></li>
13215      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13216      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13217      * </ul>
13218      */
13219     load : function(options){
13220         options = options || {};
13221         if(this.fireEvent("beforeload", this, options) !== false){
13222             this.storeOptions(options);
13223             var p = Roo.apply(options.params || {}, this.baseParams);
13224             // if meta was not loaded from remote source.. try requesting it.
13225             if (!this.reader.metaFromRemote) {
13226                 p._requestMeta = 1;
13227             }
13228             if(this.sortInfo && this.remoteSort){
13229                 var pn = this.paramNames;
13230                 p[pn["sort"]] = this.sortInfo.field;
13231                 p[pn["dir"]] = this.sortInfo.direction;
13232             }
13233             if (this.multiSort) {
13234                 var pn = this.paramNames;
13235                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13236             }
13237             
13238             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13239         }
13240     },
13241
13242     /**
13243      * Reloads the Record cache from the configured Proxy using the configured Reader and
13244      * the options from the last load operation performed.
13245      * @param {Object} options (optional) An object containing properties which may override the options
13246      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13247      * the most recently used options are reused).
13248      */
13249     reload : function(options){
13250         this.load(Roo.applyIf(options||{}, this.lastOptions));
13251     },
13252
13253     // private
13254     // Called as a callback by the Reader during a load operation.
13255     loadRecords : function(o, options, success){
13256         if(!o || success === false){
13257             if(success !== false){
13258                 this.fireEvent("load", this, [], options, o);
13259             }
13260             if(options.callback){
13261                 options.callback.call(options.scope || this, [], options, false);
13262             }
13263             return;
13264         }
13265         // if data returned failure - throw an exception.
13266         if (o.success === false) {
13267             // show a message if no listener is registered.
13268             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13269                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13270             }
13271             // loadmask wil be hooked into this..
13272             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13273             return;
13274         }
13275         var r = o.records, t = o.totalRecords || r.length;
13276         
13277         this.fireEvent("beforeloadadd", this, r, options, o);
13278         
13279         if(!options || options.add !== true){
13280             if(this.pruneModifiedRecords){
13281                 this.modified = [];
13282             }
13283             for(var i = 0, len = r.length; i < len; i++){
13284                 r[i].join(this);
13285             }
13286             if(this.snapshot){
13287                 this.data = this.snapshot;
13288                 delete this.snapshot;
13289             }
13290             this.data.clear();
13291             this.data.addAll(r);
13292             this.totalLength = t;
13293             this.applySort();
13294             this.fireEvent("datachanged", this);
13295         }else{
13296             this.totalLength = Math.max(t, this.data.length+r.length);
13297             this.add(r);
13298         }
13299         
13300         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13301                 
13302             var e = new Roo.data.Record({});
13303
13304             e.set(this.parent.displayField, this.parent.emptyTitle);
13305             e.set(this.parent.valueField, '');
13306
13307             this.insert(0, e);
13308         }
13309             
13310         this.fireEvent("load", this, r, options, o);
13311         if(options.callback){
13312             options.callback.call(options.scope || this, r, options, true);
13313         }
13314     },
13315
13316
13317     /**
13318      * Loads data from a passed data block. A Reader which understands the format of the data
13319      * must have been configured in the constructor.
13320      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13321      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13322      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13323      */
13324     loadData : function(o, append){
13325         var r = this.reader.readRecords(o);
13326         this.loadRecords(r, {add: append}, true);
13327     },
13328     
13329      /**
13330      * using 'cn' the nested child reader read the child array into it's child stores.
13331      * @param {Object} rec The record with a 'children array
13332      */
13333     loadDataFromChildren : function(rec)
13334     {
13335         this.loadData(this.reader.toLoadData(rec));
13336     },
13337     
13338
13339     /**
13340      * Gets the number of cached records.
13341      * <p>
13342      * <em>If using paging, this may not be the total size of the dataset. If the data object
13343      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13344      * the data set size</em>
13345      */
13346     getCount : function(){
13347         return this.data.length || 0;
13348     },
13349
13350     /**
13351      * Gets the total number of records in the dataset as returned by the server.
13352      * <p>
13353      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13354      * the dataset size</em>
13355      */
13356     getTotalCount : function(){
13357         return this.totalLength || 0;
13358     },
13359
13360     /**
13361      * Returns the sort state of the Store as an object with two properties:
13362      * <pre><code>
13363  field {String} The name of the field by which the Records are sorted
13364  direction {String} The sort order, "ASC" or "DESC"
13365      * </code></pre>
13366      */
13367     getSortState : function(){
13368         return this.sortInfo;
13369     },
13370
13371     // private
13372     applySort : function(){
13373         if(this.sortInfo && !this.remoteSort){
13374             var s = this.sortInfo, f = s.field;
13375             var st = this.fields.get(f).sortType;
13376             var fn = function(r1, r2){
13377                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13378                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13379             };
13380             this.data.sort(s.direction, fn);
13381             if(this.snapshot && this.snapshot != this.data){
13382                 this.snapshot.sort(s.direction, fn);
13383             }
13384         }
13385     },
13386
13387     /**
13388      * Sets the default sort column and order to be used by the next load operation.
13389      * @param {String} fieldName The name of the field to sort by.
13390      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13391      */
13392     setDefaultSort : function(field, dir){
13393         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13394     },
13395
13396     /**
13397      * Sort the Records.
13398      * If remote sorting is used, the sort is performed on the server, and the cache is
13399      * reloaded. If local sorting is used, the cache is sorted internally.
13400      * @param {String} fieldName The name of the field to sort by.
13401      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13402      */
13403     sort : function(fieldName, dir){
13404         var f = this.fields.get(fieldName);
13405         if(!dir){
13406             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13407             
13408             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13409                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13410             }else{
13411                 dir = f.sortDir;
13412             }
13413         }
13414         this.sortToggle[f.name] = dir;
13415         this.sortInfo = {field: f.name, direction: dir};
13416         if(!this.remoteSort){
13417             this.applySort();
13418             this.fireEvent("datachanged", this);
13419         }else{
13420             this.load(this.lastOptions);
13421         }
13422     },
13423
13424     /**
13425      * Calls the specified function for each of the Records in the cache.
13426      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13427      * Returning <em>false</em> aborts and exits the iteration.
13428      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13429      */
13430     each : function(fn, scope){
13431         this.data.each(fn, scope);
13432     },
13433
13434     /**
13435      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13436      * (e.g., during paging).
13437      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13438      */
13439     getModifiedRecords : function(){
13440         return this.modified;
13441     },
13442
13443     // private
13444     createFilterFn : function(property, value, anyMatch){
13445         if(!value.exec){ // not a regex
13446             value = String(value);
13447             if(value.length == 0){
13448                 return false;
13449             }
13450             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13451         }
13452         return function(r){
13453             return value.test(r.data[property]);
13454         };
13455     },
13456
13457     /**
13458      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13459      * @param {String} property A field on your records
13460      * @param {Number} start The record index to start at (defaults to 0)
13461      * @param {Number} end The last record index to include (defaults to length - 1)
13462      * @return {Number} The sum
13463      */
13464     sum : function(property, start, end){
13465         var rs = this.data.items, v = 0;
13466         start = start || 0;
13467         end = (end || end === 0) ? end : rs.length-1;
13468
13469         for(var i = start; i <= end; i++){
13470             v += (rs[i].data[property] || 0);
13471         }
13472         return v;
13473     },
13474
13475     /**
13476      * Filter the records by a specified property.
13477      * @param {String} field A field on your records
13478      * @param {String/RegExp} value Either a string that the field
13479      * should start with or a RegExp to test against the field
13480      * @param {Boolean} anyMatch True to match any part not just the beginning
13481      */
13482     filter : function(property, value, anyMatch){
13483         var fn = this.createFilterFn(property, value, anyMatch);
13484         return fn ? this.filterBy(fn) : this.clearFilter();
13485     },
13486
13487     /**
13488      * Filter by a function. The specified function will be called with each
13489      * record in this data source. If the function returns true the record is included,
13490      * otherwise it is filtered.
13491      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13492      * @param {Object} scope (optional) The scope of the function (defaults to this)
13493      */
13494     filterBy : function(fn, scope){
13495         this.snapshot = this.snapshot || this.data;
13496         this.data = this.queryBy(fn, scope||this);
13497         this.fireEvent("datachanged", this);
13498     },
13499
13500     /**
13501      * Query the records by a specified property.
13502      * @param {String} field A field on your records
13503      * @param {String/RegExp} value Either a string that the field
13504      * should start with or a RegExp to test against the field
13505      * @param {Boolean} anyMatch True to match any part not just the beginning
13506      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13507      */
13508     query : function(property, value, anyMatch){
13509         var fn = this.createFilterFn(property, value, anyMatch);
13510         return fn ? this.queryBy(fn) : this.data.clone();
13511     },
13512
13513     /**
13514      * Query by a function. The specified function will be called with each
13515      * record in this data source. If the function returns true the record is included
13516      * in the results.
13517      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13518      * @param {Object} scope (optional) The scope of the function (defaults to this)
13519       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13520      **/
13521     queryBy : function(fn, scope){
13522         var data = this.snapshot || this.data;
13523         return data.filterBy(fn, scope||this);
13524     },
13525
13526     /**
13527      * Collects unique values for a particular dataIndex from this store.
13528      * @param {String} dataIndex The property to collect
13529      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13530      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13531      * @return {Array} An array of the unique values
13532      **/
13533     collect : function(dataIndex, allowNull, bypassFilter){
13534         var d = (bypassFilter === true && this.snapshot) ?
13535                 this.snapshot.items : this.data.items;
13536         var v, sv, r = [], l = {};
13537         for(var i = 0, len = d.length; i < len; i++){
13538             v = d[i].data[dataIndex];
13539             sv = String(v);
13540             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13541                 l[sv] = true;
13542                 r[r.length] = v;
13543             }
13544         }
13545         return r;
13546     },
13547
13548     /**
13549      * Revert to a view of the Record cache with no filtering applied.
13550      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13551      */
13552     clearFilter : function(suppressEvent){
13553         if(this.snapshot && this.snapshot != this.data){
13554             this.data = this.snapshot;
13555             delete this.snapshot;
13556             if(suppressEvent !== true){
13557                 this.fireEvent("datachanged", this);
13558             }
13559         }
13560     },
13561
13562     // private
13563     afterEdit : function(record){
13564         if(this.modified.indexOf(record) == -1){
13565             this.modified.push(record);
13566         }
13567         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13568     },
13569     
13570     // private
13571     afterReject : function(record){
13572         this.modified.remove(record);
13573         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13574     },
13575
13576     // private
13577     afterCommit : function(record){
13578         this.modified.remove(record);
13579         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13580     },
13581
13582     /**
13583      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13584      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13585      */
13586     commitChanges : function(){
13587         var m = this.modified.slice(0);
13588         this.modified = [];
13589         for(var i = 0, len = m.length; i < len; i++){
13590             m[i].commit();
13591         }
13592     },
13593
13594     /**
13595      * Cancel outstanding changes on all changed records.
13596      */
13597     rejectChanges : function(){
13598         var m = this.modified.slice(0);
13599         this.modified = [];
13600         for(var i = 0, len = m.length; i < len; i++){
13601             m[i].reject();
13602         }
13603     },
13604
13605     onMetaChange : function(meta, rtype, o){
13606         this.recordType = rtype;
13607         this.fields = rtype.prototype.fields;
13608         delete this.snapshot;
13609         this.sortInfo = meta.sortInfo || this.sortInfo;
13610         this.modified = [];
13611         this.fireEvent('metachange', this, this.reader.meta);
13612     },
13613     
13614     moveIndex : function(data, type)
13615     {
13616         var index = this.indexOf(data);
13617         
13618         var newIndex = index + type;
13619         
13620         this.remove(data);
13621         
13622         this.insert(newIndex, data);
13623         
13624     }
13625 });/*
13626  * Based on:
13627  * Ext JS Library 1.1.1
13628  * Copyright(c) 2006-2007, Ext JS, LLC.
13629  *
13630  * Originally Released Under LGPL - original licence link has changed is not relivant.
13631  *
13632  * Fork - LGPL
13633  * <script type="text/javascript">
13634  */
13635
13636 /**
13637  * @class Roo.data.SimpleStore
13638  * @extends Roo.data.Store
13639  * Small helper class to make creating Stores from Array data easier.
13640  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13641  * @cfg {Array} fields An array of field definition objects, or field name strings.
13642  * @cfg {Object} an existing reader (eg. copied from another store)
13643  * @cfg {Array} data The multi-dimensional array of data
13644  * @constructor
13645  * @param {Object} config
13646  */
13647 Roo.data.SimpleStore = function(config)
13648 {
13649     Roo.data.SimpleStore.superclass.constructor.call(this, {
13650         isLocal : true,
13651         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13652                 id: config.id
13653             },
13654             Roo.data.Record.create(config.fields)
13655         ),
13656         proxy : new Roo.data.MemoryProxy(config.data)
13657     });
13658     this.load();
13659 };
13660 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13661  * Based on:
13662  * Ext JS Library 1.1.1
13663  * Copyright(c) 2006-2007, Ext JS, LLC.
13664  *
13665  * Originally Released Under LGPL - original licence link has changed is not relivant.
13666  *
13667  * Fork - LGPL
13668  * <script type="text/javascript">
13669  */
13670
13671 /**
13672 /**
13673  * @extends Roo.data.Store
13674  * @class Roo.data.JsonStore
13675  * Small helper class to make creating Stores for JSON data easier. <br/>
13676 <pre><code>
13677 var store = new Roo.data.JsonStore({
13678     url: 'get-images.php',
13679     root: 'images',
13680     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13681 });
13682 </code></pre>
13683  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13684  * JsonReader and HttpProxy (unless inline data is provided).</b>
13685  * @cfg {Array} fields An array of field definition objects, or field name strings.
13686  * @constructor
13687  * @param {Object} config
13688  */
13689 Roo.data.JsonStore = function(c){
13690     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13691         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13692         reader: new Roo.data.JsonReader(c, c.fields)
13693     }));
13694 };
13695 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13696  * Based on:
13697  * Ext JS Library 1.1.1
13698  * Copyright(c) 2006-2007, Ext JS, LLC.
13699  *
13700  * Originally Released Under LGPL - original licence link has changed is not relivant.
13701  *
13702  * Fork - LGPL
13703  * <script type="text/javascript">
13704  */
13705
13706  
13707 Roo.data.Field = function(config){
13708     if(typeof config == "string"){
13709         config = {name: config};
13710     }
13711     Roo.apply(this, config);
13712     
13713     if(!this.type){
13714         this.type = "auto";
13715     }
13716     
13717     var st = Roo.data.SortTypes;
13718     // named sortTypes are supported, here we look them up
13719     if(typeof this.sortType == "string"){
13720         this.sortType = st[this.sortType];
13721     }
13722     
13723     // set default sortType for strings and dates
13724     if(!this.sortType){
13725         switch(this.type){
13726             case "string":
13727                 this.sortType = st.asUCString;
13728                 break;
13729             case "date":
13730                 this.sortType = st.asDate;
13731                 break;
13732             default:
13733                 this.sortType = st.none;
13734         }
13735     }
13736
13737     // define once
13738     var stripRe = /[\$,%]/g;
13739
13740     // prebuilt conversion function for this field, instead of
13741     // switching every time we're reading a value
13742     if(!this.convert){
13743         var cv, dateFormat = this.dateFormat;
13744         switch(this.type){
13745             case "":
13746             case "auto":
13747             case undefined:
13748                 cv = function(v){ return v; };
13749                 break;
13750             case "string":
13751                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13752                 break;
13753             case "int":
13754                 cv = function(v){
13755                     return v !== undefined && v !== null && v !== '' ?
13756                            parseInt(String(v).replace(stripRe, ""), 10) : '';
13757                     };
13758                 break;
13759             case "float":
13760                 cv = function(v){
13761                     return v !== undefined && v !== null && v !== '' ?
13762                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
13763                     };
13764                 break;
13765             case "bool":
13766             case "boolean":
13767                 cv = function(v){ return v === true || v === "true" || v == 1; };
13768                 break;
13769             case "date":
13770                 cv = function(v){
13771                     if(!v){
13772                         return '';
13773                     }
13774                     if(v instanceof Date){
13775                         return v;
13776                     }
13777                     if(dateFormat){
13778                         if(dateFormat == "timestamp"){
13779                             return new Date(v*1000);
13780                         }
13781                         return Date.parseDate(v, dateFormat);
13782                     }
13783                     var parsed = Date.parse(v);
13784                     return parsed ? new Date(parsed) : null;
13785                 };
13786              break;
13787             
13788         }
13789         this.convert = cv;
13790     }
13791 };
13792
13793 Roo.data.Field.prototype = {
13794     dateFormat: null,
13795     defaultValue: "",
13796     mapping: null,
13797     sortType : null,
13798     sortDir : "ASC"
13799 };/*
13800  * Based on:
13801  * Ext JS Library 1.1.1
13802  * Copyright(c) 2006-2007, Ext JS, LLC.
13803  *
13804  * Originally Released Under LGPL - original licence link has changed is not relivant.
13805  *
13806  * Fork - LGPL
13807  * <script type="text/javascript">
13808  */
13809  
13810 // Base class for reading structured data from a data source.  This class is intended to be
13811 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13812
13813 /**
13814  * @class Roo.data.DataReader
13815  * Base class for reading structured data from a data source.  This class is intended to be
13816  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13817  */
13818
13819 Roo.data.DataReader = function(meta, recordType){
13820     
13821     this.meta = meta;
13822     
13823     this.recordType = recordType instanceof Array ? 
13824         Roo.data.Record.create(recordType) : recordType;
13825 };
13826
13827 Roo.data.DataReader.prototype = {
13828     
13829     
13830     readerType : 'Data',
13831      /**
13832      * Create an empty record
13833      * @param {Object} data (optional) - overlay some values
13834      * @return {Roo.data.Record} record created.
13835      */
13836     newRow :  function(d) {
13837         var da =  {};
13838         this.recordType.prototype.fields.each(function(c) {
13839             switch( c.type) {
13840                 case 'int' : da[c.name] = 0; break;
13841                 case 'date' : da[c.name] = new Date(); break;
13842                 case 'float' : da[c.name] = 0.0; break;
13843                 case 'boolean' : da[c.name] = false; break;
13844                 default : da[c.name] = ""; break;
13845             }
13846             
13847         });
13848         return new this.recordType(Roo.apply(da, d));
13849     }
13850     
13851     
13852 };/*
13853  * Based on:
13854  * Ext JS Library 1.1.1
13855  * Copyright(c) 2006-2007, Ext JS, LLC.
13856  *
13857  * Originally Released Under LGPL - original licence link has changed is not relivant.
13858  *
13859  * Fork - LGPL
13860  * <script type="text/javascript">
13861  */
13862
13863 /**
13864  * @class Roo.data.DataProxy
13865  * @extends Roo.data.Observable
13866  * This class is an abstract base class for implementations which provide retrieval of
13867  * unformatted data objects.<br>
13868  * <p>
13869  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13870  * (of the appropriate type which knows how to parse the data object) to provide a block of
13871  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13872  * <p>
13873  * Custom implementations must implement the load method as described in
13874  * {@link Roo.data.HttpProxy#load}.
13875  */
13876 Roo.data.DataProxy = function(){
13877     this.addEvents({
13878         /**
13879          * @event beforeload
13880          * Fires before a network request is made to retrieve a data object.
13881          * @param {Object} This DataProxy object.
13882          * @param {Object} params The params parameter to the load function.
13883          */
13884         beforeload : true,
13885         /**
13886          * @event load
13887          * Fires before the load method's callback is called.
13888          * @param {Object} This DataProxy object.
13889          * @param {Object} o The data object.
13890          * @param {Object} arg The callback argument object passed to the load function.
13891          */
13892         load : true,
13893         /**
13894          * @event loadexception
13895          * Fires if an Exception occurs during data retrieval.
13896          * @param {Object} This DataProxy object.
13897          * @param {Object} o The data object.
13898          * @param {Object} arg The callback argument object passed to the load function.
13899          * @param {Object} e The Exception.
13900          */
13901         loadexception : true
13902     });
13903     Roo.data.DataProxy.superclass.constructor.call(this);
13904 };
13905
13906 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
13907
13908     /**
13909      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
13910      */
13911 /*
13912  * Based on:
13913  * Ext JS Library 1.1.1
13914  * Copyright(c) 2006-2007, Ext JS, LLC.
13915  *
13916  * Originally Released Under LGPL - original licence link has changed is not relivant.
13917  *
13918  * Fork - LGPL
13919  * <script type="text/javascript">
13920  */
13921 /**
13922  * @class Roo.data.MemoryProxy
13923  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
13924  * to the Reader when its load method is called.
13925  * @constructor
13926  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
13927  */
13928 Roo.data.MemoryProxy = function(data){
13929     if (data.data) {
13930         data = data.data;
13931     }
13932     Roo.data.MemoryProxy.superclass.constructor.call(this);
13933     this.data = data;
13934 };
13935
13936 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
13937     
13938     /**
13939      * Load data from the requested source (in this case an in-memory
13940      * data object passed to the constructor), read the data object into
13941      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13942      * process that block using the passed callback.
13943      * @param {Object} params This parameter is not used by the MemoryProxy class.
13944      * @param {Roo.data.DataReader} reader The Reader object which converts the data
13945      * object into a block of Roo.data.Records.
13946      * @param {Function} callback The function into which to pass the block of Roo.data.records.
13947      * The function must be passed <ul>
13948      * <li>The Record block object</li>
13949      * <li>The "arg" argument from the load function</li>
13950      * <li>A boolean success indicator</li>
13951      * </ul>
13952      * @param {Object} scope The scope in which to call the callback
13953      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13954      */
13955     load : function(params, reader, callback, scope, arg){
13956         params = params || {};
13957         var result;
13958         try {
13959             result = reader.readRecords(params.data ? params.data :this.data);
13960         }catch(e){
13961             this.fireEvent("loadexception", this, arg, null, e);
13962             callback.call(scope, null, arg, false);
13963             return;
13964         }
13965         callback.call(scope, result, arg, true);
13966     },
13967     
13968     // private
13969     update : function(params, records){
13970         
13971     }
13972 });/*
13973  * Based on:
13974  * Ext JS Library 1.1.1
13975  * Copyright(c) 2006-2007, Ext JS, LLC.
13976  *
13977  * Originally Released Under LGPL - original licence link has changed is not relivant.
13978  *
13979  * Fork - LGPL
13980  * <script type="text/javascript">
13981  */
13982 /**
13983  * @class Roo.data.HttpProxy
13984  * @extends Roo.data.DataProxy
13985  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
13986  * configured to reference a certain URL.<br><br>
13987  * <p>
13988  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
13989  * from which the running page was served.<br><br>
13990  * <p>
13991  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
13992  * <p>
13993  * Be aware that to enable the browser to parse an XML document, the server must set
13994  * the Content-Type header in the HTTP response to "text/xml".
13995  * @constructor
13996  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
13997  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
13998  * will be used to make the request.
13999  */
14000 Roo.data.HttpProxy = function(conn){
14001     Roo.data.HttpProxy.superclass.constructor.call(this);
14002     // is conn a conn config or a real conn?
14003     this.conn = conn;
14004     this.useAjax = !conn || !conn.events;
14005   
14006 };
14007
14008 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14009     // thse are take from connection...
14010     
14011     /**
14012      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14013      */
14014     /**
14015      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14016      * extra parameters to each request made by this object. (defaults to undefined)
14017      */
14018     /**
14019      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14020      *  to each request made by this object. (defaults to undefined)
14021      */
14022     /**
14023      * @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)
14024      */
14025     /**
14026      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14027      */
14028      /**
14029      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14030      * @type Boolean
14031      */
14032   
14033
14034     /**
14035      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14036      * @type Boolean
14037      */
14038     /**
14039      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14040      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14041      * a finer-grained basis than the DataProxy events.
14042      */
14043     getConnection : function(){
14044         return this.useAjax ? Roo.Ajax : this.conn;
14045     },
14046
14047     /**
14048      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14049      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14050      * process that block using the passed callback.
14051      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14052      * for the request to the remote server.
14053      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14054      * object into a block of Roo.data.Records.
14055      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14056      * The function must be passed <ul>
14057      * <li>The Record block object</li>
14058      * <li>The "arg" argument from the load function</li>
14059      * <li>A boolean success indicator</li>
14060      * </ul>
14061      * @param {Object} scope The scope in which to call the callback
14062      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14063      */
14064     load : function(params, reader, callback, scope, arg){
14065         if(this.fireEvent("beforeload", this, params) !== false){
14066             var  o = {
14067                 params : params || {},
14068                 request: {
14069                     callback : callback,
14070                     scope : scope,
14071                     arg : arg
14072                 },
14073                 reader: reader,
14074                 callback : this.loadResponse,
14075                 scope: this
14076             };
14077             if(this.useAjax){
14078                 Roo.applyIf(o, this.conn);
14079                 if(this.activeRequest){
14080                     Roo.Ajax.abort(this.activeRequest);
14081                 }
14082                 this.activeRequest = Roo.Ajax.request(o);
14083             }else{
14084                 this.conn.request(o);
14085             }
14086         }else{
14087             callback.call(scope||this, null, arg, false);
14088         }
14089     },
14090
14091     // private
14092     loadResponse : function(o, success, response){
14093         delete this.activeRequest;
14094         if(!success){
14095             this.fireEvent("loadexception", this, o, response);
14096             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14097             return;
14098         }
14099         var result;
14100         try {
14101             result = o.reader.read(response);
14102         }catch(e){
14103             this.fireEvent("loadexception", this, o, response, e);
14104             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14105             return;
14106         }
14107         
14108         this.fireEvent("load", this, o, o.request.arg);
14109         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14110     },
14111
14112     // private
14113     update : function(dataSet){
14114
14115     },
14116
14117     // private
14118     updateResponse : function(dataSet){
14119
14120     }
14121 });/*
14122  * Based on:
14123  * Ext JS Library 1.1.1
14124  * Copyright(c) 2006-2007, Ext JS, LLC.
14125  *
14126  * Originally Released Under LGPL - original licence link has changed is not relivant.
14127  *
14128  * Fork - LGPL
14129  * <script type="text/javascript">
14130  */
14131
14132 /**
14133  * @class Roo.data.ScriptTagProxy
14134  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14135  * other than the originating domain of the running page.<br><br>
14136  * <p>
14137  * <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
14138  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14139  * <p>
14140  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14141  * source code that is used as the source inside a &lt;script> tag.<br><br>
14142  * <p>
14143  * In order for the browser to process the returned data, the server must wrap the data object
14144  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14145  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14146  * depending on whether the callback name was passed:
14147  * <p>
14148  * <pre><code>
14149 boolean scriptTag = false;
14150 String cb = request.getParameter("callback");
14151 if (cb != null) {
14152     scriptTag = true;
14153     response.setContentType("text/javascript");
14154 } else {
14155     response.setContentType("application/x-json");
14156 }
14157 Writer out = response.getWriter();
14158 if (scriptTag) {
14159     out.write(cb + "(");
14160 }
14161 out.print(dataBlock.toJsonString());
14162 if (scriptTag) {
14163     out.write(");");
14164 }
14165 </pre></code>
14166  *
14167  * @constructor
14168  * @param {Object} config A configuration object.
14169  */
14170 Roo.data.ScriptTagProxy = function(config){
14171     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14172     Roo.apply(this, config);
14173     this.head = document.getElementsByTagName("head")[0];
14174 };
14175
14176 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14177
14178 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14179     /**
14180      * @cfg {String} url The URL from which to request the data object.
14181      */
14182     /**
14183      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14184      */
14185     timeout : 30000,
14186     /**
14187      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14188      * the server the name of the callback function set up by the load call to process the returned data object.
14189      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14190      * javascript output which calls this named function passing the data object as its only parameter.
14191      */
14192     callbackParam : "callback",
14193     /**
14194      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14195      * name to the request.
14196      */
14197     nocache : true,
14198
14199     /**
14200      * Load data from the configured URL, read the data object into
14201      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14202      * process that block using the passed callback.
14203      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14204      * for the request to the remote server.
14205      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14206      * object into a block of Roo.data.Records.
14207      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14208      * The function must be passed <ul>
14209      * <li>The Record block object</li>
14210      * <li>The "arg" argument from the load function</li>
14211      * <li>A boolean success indicator</li>
14212      * </ul>
14213      * @param {Object} scope The scope in which to call the callback
14214      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14215      */
14216     load : function(params, reader, callback, scope, arg){
14217         if(this.fireEvent("beforeload", this, params) !== false){
14218
14219             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14220
14221             var url = this.url;
14222             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14223             if(this.nocache){
14224                 url += "&_dc=" + (new Date().getTime());
14225             }
14226             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14227             var trans = {
14228                 id : transId,
14229                 cb : "stcCallback"+transId,
14230                 scriptId : "stcScript"+transId,
14231                 params : params,
14232                 arg : arg,
14233                 url : url,
14234                 callback : callback,
14235                 scope : scope,
14236                 reader : reader
14237             };
14238             var conn = this;
14239
14240             window[trans.cb] = function(o){
14241                 conn.handleResponse(o, trans);
14242             };
14243
14244             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14245
14246             if(this.autoAbort !== false){
14247                 this.abort();
14248             }
14249
14250             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14251
14252             var script = document.createElement("script");
14253             script.setAttribute("src", url);
14254             script.setAttribute("type", "text/javascript");
14255             script.setAttribute("id", trans.scriptId);
14256             this.head.appendChild(script);
14257
14258             this.trans = trans;
14259         }else{
14260             callback.call(scope||this, null, arg, false);
14261         }
14262     },
14263
14264     // private
14265     isLoading : function(){
14266         return this.trans ? true : false;
14267     },
14268
14269     /**
14270      * Abort the current server request.
14271      */
14272     abort : function(){
14273         if(this.isLoading()){
14274             this.destroyTrans(this.trans);
14275         }
14276     },
14277
14278     // private
14279     destroyTrans : function(trans, isLoaded){
14280         this.head.removeChild(document.getElementById(trans.scriptId));
14281         clearTimeout(trans.timeoutId);
14282         if(isLoaded){
14283             window[trans.cb] = undefined;
14284             try{
14285                 delete window[trans.cb];
14286             }catch(e){}
14287         }else{
14288             // if hasn't been loaded, wait for load to remove it to prevent script error
14289             window[trans.cb] = function(){
14290                 window[trans.cb] = undefined;
14291                 try{
14292                     delete window[trans.cb];
14293                 }catch(e){}
14294             };
14295         }
14296     },
14297
14298     // private
14299     handleResponse : function(o, trans){
14300         this.trans = false;
14301         this.destroyTrans(trans, true);
14302         var result;
14303         try {
14304             result = trans.reader.readRecords(o);
14305         }catch(e){
14306             this.fireEvent("loadexception", this, o, trans.arg, e);
14307             trans.callback.call(trans.scope||window, null, trans.arg, false);
14308             return;
14309         }
14310         this.fireEvent("load", this, o, trans.arg);
14311         trans.callback.call(trans.scope||window, result, trans.arg, true);
14312     },
14313
14314     // private
14315     handleFailure : function(trans){
14316         this.trans = false;
14317         this.destroyTrans(trans, false);
14318         this.fireEvent("loadexception", this, null, trans.arg);
14319         trans.callback.call(trans.scope||window, null, trans.arg, false);
14320     }
14321 });/*
14322  * Based on:
14323  * Ext JS Library 1.1.1
14324  * Copyright(c) 2006-2007, Ext JS, LLC.
14325  *
14326  * Originally Released Under LGPL - original licence link has changed is not relivant.
14327  *
14328  * Fork - LGPL
14329  * <script type="text/javascript">
14330  */
14331
14332 /**
14333  * @class Roo.data.JsonReader
14334  * @extends Roo.data.DataReader
14335  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14336  * based on mappings in a provided Roo.data.Record constructor.
14337  * 
14338  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14339  * in the reply previously. 
14340  * 
14341  * <p>
14342  * Example code:
14343  * <pre><code>
14344 var RecordDef = Roo.data.Record.create([
14345     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14346     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14347 ]);
14348 var myReader = new Roo.data.JsonReader({
14349     totalProperty: "results",    // The property which contains the total dataset size (optional)
14350     root: "rows",                // The property which contains an Array of row objects
14351     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14352 }, RecordDef);
14353 </code></pre>
14354  * <p>
14355  * This would consume a JSON file like this:
14356  * <pre><code>
14357 { 'results': 2, 'rows': [
14358     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14359     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14360 }
14361 </code></pre>
14362  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14363  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14364  * paged from the remote server.
14365  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14366  * @cfg {String} root name of the property which contains the Array of row objects.
14367  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14368  * @cfg {Array} fields Array of field definition objects
14369  * @constructor
14370  * Create a new JsonReader
14371  * @param {Object} meta Metadata configuration options
14372  * @param {Object} recordType Either an Array of field definition objects,
14373  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14374  */
14375 Roo.data.JsonReader = function(meta, recordType){
14376     
14377     meta = meta || {};
14378     // set some defaults:
14379     Roo.applyIf(meta, {
14380         totalProperty: 'total',
14381         successProperty : 'success',
14382         root : 'data',
14383         id : 'id'
14384     });
14385     
14386     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14387 };
14388 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14389     
14390     readerType : 'Json',
14391     
14392     /**
14393      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14394      * Used by Store query builder to append _requestMeta to params.
14395      * 
14396      */
14397     metaFromRemote : false,
14398     /**
14399      * This method is only used by a DataProxy which has retrieved data from a remote server.
14400      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14401      * @return {Object} data A data block which is used by an Roo.data.Store object as
14402      * a cache of Roo.data.Records.
14403      */
14404     read : function(response){
14405         var json = response.responseText;
14406        
14407         var o = /* eval:var:o */ eval("("+json+")");
14408         if(!o) {
14409             throw {message: "JsonReader.read: Json object not found"};
14410         }
14411         
14412         if(o.metaData){
14413             
14414             delete this.ef;
14415             this.metaFromRemote = true;
14416             this.meta = o.metaData;
14417             this.recordType = Roo.data.Record.create(o.metaData.fields);
14418             this.onMetaChange(this.meta, this.recordType, o);
14419         }
14420         return this.readRecords(o);
14421     },
14422
14423     // private function a store will implement
14424     onMetaChange : function(meta, recordType, o){
14425
14426     },
14427
14428     /**
14429          * @ignore
14430          */
14431     simpleAccess: function(obj, subsc) {
14432         return obj[subsc];
14433     },
14434
14435         /**
14436          * @ignore
14437          */
14438     getJsonAccessor: function(){
14439         var re = /[\[\.]/;
14440         return function(expr) {
14441             try {
14442                 return(re.test(expr))
14443                     ? new Function("obj", "return obj." + expr)
14444                     : function(obj){
14445                         return obj[expr];
14446                     };
14447             } catch(e){}
14448             return Roo.emptyFn;
14449         };
14450     }(),
14451
14452     /**
14453      * Create a data block containing Roo.data.Records from an XML document.
14454      * @param {Object} o An object which contains an Array of row objects in the property specified
14455      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14456      * which contains the total size of the dataset.
14457      * @return {Object} data A data block which is used by an Roo.data.Store object as
14458      * a cache of Roo.data.Records.
14459      */
14460     readRecords : function(o){
14461         /**
14462          * After any data loads, the raw JSON data is available for further custom processing.
14463          * @type Object
14464          */
14465         this.o = o;
14466         var s = this.meta, Record = this.recordType,
14467             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14468
14469 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14470         if (!this.ef) {
14471             if(s.totalProperty) {
14472                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14473                 }
14474                 if(s.successProperty) {
14475                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14476                 }
14477                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14478                 if (s.id) {
14479                         var g = this.getJsonAccessor(s.id);
14480                         this.getId = function(rec) {
14481                                 var r = g(rec);  
14482                                 return (r === undefined || r === "") ? null : r;
14483                         };
14484                 } else {
14485                         this.getId = function(){return null;};
14486                 }
14487             this.ef = [];
14488             for(var jj = 0; jj < fl; jj++){
14489                 f = fi[jj];
14490                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14491                 this.ef[jj] = this.getJsonAccessor(map);
14492             }
14493         }
14494
14495         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14496         if(s.totalProperty){
14497             var vt = parseInt(this.getTotal(o), 10);
14498             if(!isNaN(vt)){
14499                 totalRecords = vt;
14500             }
14501         }
14502         if(s.successProperty){
14503             var vs = this.getSuccess(o);
14504             if(vs === false || vs === 'false'){
14505                 success = false;
14506             }
14507         }
14508         var records = [];
14509         for(var i = 0; i < c; i++){
14510                 var n = root[i];
14511             var values = {};
14512             var id = this.getId(n);
14513             for(var j = 0; j < fl; j++){
14514                 f = fi[j];
14515             var v = this.ef[j](n);
14516             if (!f.convert) {
14517                 Roo.log('missing convert for ' + f.name);
14518                 Roo.log(f);
14519                 continue;
14520             }
14521             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14522             }
14523             var record = new Record(values, id);
14524             record.json = n;
14525             records[i] = record;
14526         }
14527         return {
14528             raw : o,
14529             success : success,
14530             records : records,
14531             totalRecords : totalRecords
14532         };
14533     },
14534     // used when loading children.. @see loadDataFromChildren
14535     toLoadData: function(rec)
14536     {
14537         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14538         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14539         return { data : data, total : data.length };
14540         
14541     }
14542 });/*
14543  * Based on:
14544  * Ext JS Library 1.1.1
14545  * Copyright(c) 2006-2007, Ext JS, LLC.
14546  *
14547  * Originally Released Under LGPL - original licence link has changed is not relivant.
14548  *
14549  * Fork - LGPL
14550  * <script type="text/javascript">
14551  */
14552
14553 /**
14554  * @class Roo.data.ArrayReader
14555  * @extends Roo.data.DataReader
14556  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14557  * Each element of that Array represents a row of data fields. The
14558  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14559  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14560  * <p>
14561  * Example code:.
14562  * <pre><code>
14563 var RecordDef = Roo.data.Record.create([
14564     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14565     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14566 ]);
14567 var myReader = new Roo.data.ArrayReader({
14568     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14569 }, RecordDef);
14570 </code></pre>
14571  * <p>
14572  * This would consume an Array like this:
14573  * <pre><code>
14574 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14575   </code></pre>
14576  
14577  * @constructor
14578  * Create a new JsonReader
14579  * @param {Object} meta Metadata configuration options.
14580  * @param {Object|Array} recordType Either an Array of field definition objects
14581  * 
14582  * @cfg {Array} fields Array of field definition objects
14583  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14584  * as specified to {@link Roo.data.Record#create},
14585  * or an {@link Roo.data.Record} object
14586  *
14587  * 
14588  * created using {@link Roo.data.Record#create}.
14589  */
14590 Roo.data.ArrayReader = function(meta, recordType)
14591 {    
14592     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14593 };
14594
14595 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14596     
14597       /**
14598      * Create a data block containing Roo.data.Records from an XML document.
14599      * @param {Object} o An Array of row objects which represents the dataset.
14600      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14601      * a cache of Roo.data.Records.
14602      */
14603     readRecords : function(o)
14604     {
14605         var sid = this.meta ? this.meta.id : null;
14606         var recordType = this.recordType, fields = recordType.prototype.fields;
14607         var records = [];
14608         var root = o;
14609         for(var i = 0; i < root.length; i++){
14610                 var n = root[i];
14611             var values = {};
14612             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14613             for(var j = 0, jlen = fields.length; j < jlen; j++){
14614                 var f = fields.items[j];
14615                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14616                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14617                 v = f.convert(v);
14618                 values[f.name] = v;
14619             }
14620             var record = new recordType(values, id);
14621             record.json = n;
14622             records[records.length] = record;
14623         }
14624         return {
14625             records : records,
14626             totalRecords : records.length
14627         };
14628     },
14629     // used when loading children.. @see loadDataFromChildren
14630     toLoadData: function(rec)
14631     {
14632         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14633         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14634         
14635     }
14636     
14637     
14638 });/*
14639  * - LGPL
14640  * * 
14641  */
14642
14643 /**
14644  * @class Roo.bootstrap.ComboBox
14645  * @extends Roo.bootstrap.TriggerField
14646  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14647  * @cfg {Boolean} append (true|false) default false
14648  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14649  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14650  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14651  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14652  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14653  * @cfg {Boolean} animate default true
14654  * @cfg {Boolean} emptyResultText only for touch device
14655  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14656  * @cfg {String} emptyTitle default ''
14657  * @constructor
14658  * Create a new ComboBox.
14659  * @param {Object} config Configuration options
14660  */
14661 Roo.bootstrap.ComboBox = function(config){
14662     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14663     this.addEvents({
14664         /**
14665          * @event expand
14666          * Fires when the dropdown list is expanded
14667         * @param {Roo.bootstrap.ComboBox} combo This combo box
14668         */
14669         'expand' : true,
14670         /**
14671          * @event collapse
14672          * Fires when the dropdown list is collapsed
14673         * @param {Roo.bootstrap.ComboBox} combo This combo box
14674         */
14675         'collapse' : true,
14676         /**
14677          * @event beforeselect
14678          * Fires before a list item is selected. Return false to cancel the selection.
14679         * @param {Roo.bootstrap.ComboBox} combo This combo box
14680         * @param {Roo.data.Record} record The data record returned from the underlying store
14681         * @param {Number} index The index of the selected item in the dropdown list
14682         */
14683         'beforeselect' : true,
14684         /**
14685          * @event select
14686          * Fires when a list item is selected
14687         * @param {Roo.bootstrap.ComboBox} combo This combo box
14688         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14689         * @param {Number} index The index of the selected item in the dropdown list
14690         */
14691         'select' : true,
14692         /**
14693          * @event beforequery
14694          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14695          * The event object passed has these properties:
14696         * @param {Roo.bootstrap.ComboBox} combo This combo box
14697         * @param {String} query The query
14698         * @param {Boolean} forceAll true to force "all" query
14699         * @param {Boolean} cancel true to cancel the query
14700         * @param {Object} e The query event object
14701         */
14702         'beforequery': true,
14703          /**
14704          * @event add
14705          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14706         * @param {Roo.bootstrap.ComboBox} combo This combo box
14707         */
14708         'add' : true,
14709         /**
14710          * @event edit
14711          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14712         * @param {Roo.bootstrap.ComboBox} combo This combo box
14713         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14714         */
14715         'edit' : true,
14716         /**
14717          * @event remove
14718          * Fires when the remove value from the combobox array
14719         * @param {Roo.bootstrap.ComboBox} combo This combo box
14720         */
14721         'remove' : true,
14722         /**
14723          * @event afterremove
14724          * Fires when the remove value from the combobox array
14725         * @param {Roo.bootstrap.ComboBox} combo This combo box
14726         */
14727         'afterremove' : true,
14728         /**
14729          * @event specialfilter
14730          * Fires when specialfilter
14731             * @param {Roo.bootstrap.ComboBox} combo This combo box
14732             */
14733         'specialfilter' : true,
14734         /**
14735          * @event tick
14736          * Fires when tick the element
14737             * @param {Roo.bootstrap.ComboBox} combo This combo box
14738             */
14739         'tick' : true,
14740         /**
14741          * @event touchviewdisplay
14742          * Fires when touch view require special display (default is using displayField)
14743             * @param {Roo.bootstrap.ComboBox} combo This combo box
14744             * @param {Object} cfg set html .
14745             */
14746         'touchviewdisplay' : true
14747         
14748     });
14749     
14750     this.item = [];
14751     this.tickItems = [];
14752     
14753     this.selectedIndex = -1;
14754     if(this.mode == 'local'){
14755         if(config.queryDelay === undefined){
14756             this.queryDelay = 10;
14757         }
14758         if(config.minChars === undefined){
14759             this.minChars = 0;
14760         }
14761     }
14762 };
14763
14764 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14765      
14766     /**
14767      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14768      * rendering into an Roo.Editor, defaults to false)
14769      */
14770     /**
14771      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14772      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14773      */
14774     /**
14775      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14776      */
14777     /**
14778      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14779      * the dropdown list (defaults to undefined, with no header element)
14780      */
14781
14782      /**
14783      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
14784      */
14785      
14786      /**
14787      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14788      */
14789     listWidth: undefined,
14790     /**
14791      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14792      * mode = 'remote' or 'text' if mode = 'local')
14793      */
14794     displayField: undefined,
14795     
14796     /**
14797      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14798      * mode = 'remote' or 'value' if mode = 'local'). 
14799      * Note: use of a valueField requires the user make a selection
14800      * in order for a value to be mapped.
14801      */
14802     valueField: undefined,
14803     /**
14804      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14805      */
14806     modalTitle : '',
14807     
14808     /**
14809      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14810      * field's data value (defaults to the underlying DOM element's name)
14811      */
14812     hiddenName: undefined,
14813     /**
14814      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14815      */
14816     listClass: '',
14817     /**
14818      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14819      */
14820     selectedClass: 'active',
14821     
14822     /**
14823      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14824      */
14825     shadow:'sides',
14826     /**
14827      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14828      * anchor positions (defaults to 'tl-bl')
14829      */
14830     listAlign: 'tl-bl?',
14831     /**
14832      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14833      */
14834     maxHeight: 300,
14835     /**
14836      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
14837      * query specified by the allQuery config option (defaults to 'query')
14838      */
14839     triggerAction: 'query',
14840     /**
14841      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14842      * (defaults to 4, does not apply if editable = false)
14843      */
14844     minChars : 4,
14845     /**
14846      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14847      * delay (typeAheadDelay) if it matches a known value (defaults to false)
14848      */
14849     typeAhead: false,
14850     /**
14851      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14852      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14853      */
14854     queryDelay: 500,
14855     /**
14856      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14857      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
14858      */
14859     pageSize: 0,
14860     /**
14861      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
14862      * when editable = true (defaults to false)
14863      */
14864     selectOnFocus:false,
14865     /**
14866      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14867      */
14868     queryParam: 'query',
14869     /**
14870      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
14871      * when mode = 'remote' (defaults to 'Loading...')
14872      */
14873     loadingText: 'Loading...',
14874     /**
14875      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14876      */
14877     resizable: false,
14878     /**
14879      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14880      */
14881     handleHeight : 8,
14882     /**
14883      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14884      * traditional select (defaults to true)
14885      */
14886     editable: true,
14887     /**
14888      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14889      */
14890     allQuery: '',
14891     /**
14892      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
14893      */
14894     mode: 'remote',
14895     /**
14896      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
14897      * listWidth has a higher value)
14898      */
14899     minListWidth : 70,
14900     /**
14901      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
14902      * allow the user to set arbitrary text into the field (defaults to false)
14903      */
14904     forceSelection:false,
14905     /**
14906      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
14907      * if typeAhead = true (defaults to 250)
14908      */
14909     typeAheadDelay : 250,
14910     /**
14911      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
14912      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
14913      */
14914     valueNotFoundText : undefined,
14915     /**
14916      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
14917      */
14918     blockFocus : false,
14919     
14920     /**
14921      * @cfg {Boolean} disableClear Disable showing of clear button.
14922      */
14923     disableClear : false,
14924     /**
14925      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
14926      */
14927     alwaysQuery : false,
14928     
14929     /**
14930      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
14931      */
14932     multiple : false,
14933     
14934     /**
14935      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
14936      */
14937     invalidClass : "has-warning",
14938     
14939     /**
14940      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
14941      */
14942     validClass : "has-success",
14943     
14944     /**
14945      * @cfg {Boolean} specialFilter (true|false) special filter default false
14946      */
14947     specialFilter : false,
14948     
14949     /**
14950      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
14951      */
14952     mobileTouchView : true,
14953     
14954     /**
14955      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
14956      */
14957     useNativeIOS : false,
14958     
14959     /**
14960      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
14961      */
14962     mobile_restrict_height : false,
14963     
14964     ios_options : false,
14965     
14966     //private
14967     addicon : false,
14968     editicon: false,
14969     
14970     page: 0,
14971     hasQuery: false,
14972     append: false,
14973     loadNext: false,
14974     autoFocus : true,
14975     tickable : false,
14976     btnPosition : 'right',
14977     triggerList : true,
14978     showToggleBtn : true,
14979     animate : true,
14980     emptyResultText: 'Empty',
14981     triggerText : 'Select',
14982     emptyTitle : '',
14983     
14984     // element that contains real text value.. (when hidden is used..)
14985     
14986     getAutoCreate : function()
14987     {   
14988         var cfg = false;
14989         //render
14990         /*
14991          * Render classic select for iso
14992          */
14993         
14994         if(Roo.isIOS && this.useNativeIOS){
14995             cfg = this.getAutoCreateNativeIOS();
14996             return cfg;
14997         }
14998         
14999         /*
15000          * Touch Devices
15001          */
15002         
15003         if(Roo.isTouch && this.mobileTouchView){
15004             cfg = this.getAutoCreateTouchView();
15005             return cfg;;
15006         }
15007         
15008         /*
15009          *  Normal ComboBox
15010          */
15011         if(!this.tickable){
15012             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15013             return cfg;
15014         }
15015         
15016         /*
15017          *  ComboBox with tickable selections
15018          */
15019              
15020         var align = this.labelAlign || this.parentLabelAlign();
15021         
15022         cfg = {
15023             cls : 'form-group roo-combobox-tickable' //input-group
15024         };
15025         
15026         var btn_text_select = '';
15027         var btn_text_done = '';
15028         var btn_text_cancel = '';
15029         
15030         if (this.btn_text_show) {
15031             btn_text_select = 'Select';
15032             btn_text_done = 'Done';
15033             btn_text_cancel = 'Cancel'; 
15034         }
15035         
15036         var buttons = {
15037             tag : 'div',
15038             cls : 'tickable-buttons',
15039             cn : [
15040                 {
15041                     tag : 'button',
15042                     type : 'button',
15043                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15044                     //html : this.triggerText
15045                     html: btn_text_select
15046                 },
15047                 {
15048                     tag : 'button',
15049                     type : 'button',
15050                     name : 'ok',
15051                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15052                     //html : 'Done'
15053                     html: btn_text_done
15054                 },
15055                 {
15056                     tag : 'button',
15057                     type : 'button',
15058                     name : 'cancel',
15059                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15060                     //html : 'Cancel'
15061                     html: btn_text_cancel
15062                 }
15063             ]
15064         };
15065         
15066         if(this.editable){
15067             buttons.cn.unshift({
15068                 tag: 'input',
15069                 cls: 'roo-select2-search-field-input'
15070             });
15071         }
15072         
15073         var _this = this;
15074         
15075         Roo.each(buttons.cn, function(c){
15076             if (_this.size) {
15077                 c.cls += ' btn-' + _this.size;
15078             }
15079
15080             if (_this.disabled) {
15081                 c.disabled = true;
15082             }
15083         });
15084         
15085         var box = {
15086             tag: 'div',
15087             style : 'display: contents',
15088             cn: [
15089                 {
15090                     tag: 'input',
15091                     type : 'hidden',
15092                     cls: 'form-hidden-field'
15093                 },
15094                 {
15095                     tag: 'ul',
15096                     cls: 'roo-select2-choices',
15097                     cn:[
15098                         {
15099                             tag: 'li',
15100                             cls: 'roo-select2-search-field',
15101                             cn: [
15102                                 buttons
15103                             ]
15104                         }
15105                     ]
15106                 }
15107             ]
15108         };
15109         
15110         var combobox = {
15111             cls: 'roo-select2-container input-group roo-select2-container-multi',
15112             cn: [
15113                 
15114                 box
15115 //                {
15116 //                    tag: 'ul',
15117 //                    cls: 'typeahead typeahead-long dropdown-menu',
15118 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15119 //                }
15120             ]
15121         };
15122         
15123         if(this.hasFeedback && !this.allowBlank){
15124             
15125             var feedback = {
15126                 tag: 'span',
15127                 cls: 'glyphicon form-control-feedback'
15128             };
15129
15130             combobox.cn.push(feedback);
15131         }
15132         
15133         
15134         
15135         var indicator = {
15136             tag : 'i',
15137             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15138             tooltip : 'This field is required'
15139         };
15140         if (Roo.bootstrap.version == 4) {
15141             indicator = {
15142                 tag : 'i',
15143                 style : 'display:none'
15144             };
15145         }
15146         if (align ==='left' && this.fieldLabel.length) {
15147             
15148             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15149             
15150             cfg.cn = [
15151                 indicator,
15152                 {
15153                     tag: 'label',
15154                     'for' :  id,
15155                     cls : 'control-label col-form-label',
15156                     html : this.fieldLabel
15157
15158                 },
15159                 {
15160                     cls : "", 
15161                     cn: [
15162                         combobox
15163                     ]
15164                 }
15165
15166             ];
15167             
15168             var labelCfg = cfg.cn[1];
15169             var contentCfg = cfg.cn[2];
15170             
15171
15172             if(this.indicatorpos == 'right'){
15173                 
15174                 cfg.cn = [
15175                     {
15176                         tag: 'label',
15177                         'for' :  id,
15178                         cls : 'control-label col-form-label',
15179                         cn : [
15180                             {
15181                                 tag : 'span',
15182                                 html : this.fieldLabel
15183                             },
15184                             indicator
15185                         ]
15186                     },
15187                     {
15188                         cls : "",
15189                         cn: [
15190                             combobox
15191                         ]
15192                     }
15193
15194                 ];
15195                 
15196                 
15197                 
15198                 labelCfg = cfg.cn[0];
15199                 contentCfg = cfg.cn[1];
15200             
15201             }
15202             
15203             if(this.labelWidth > 12){
15204                 labelCfg.style = "width: " + this.labelWidth + 'px';
15205             }
15206             
15207             if(this.labelWidth < 13 && this.labelmd == 0){
15208                 this.labelmd = this.labelWidth;
15209             }
15210             
15211             if(this.labellg > 0){
15212                 labelCfg.cls += ' col-lg-' + this.labellg;
15213                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15214             }
15215             
15216             if(this.labelmd > 0){
15217                 labelCfg.cls += ' col-md-' + this.labelmd;
15218                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15219             }
15220             
15221             if(this.labelsm > 0){
15222                 labelCfg.cls += ' col-sm-' + this.labelsm;
15223                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15224             }
15225             
15226             if(this.labelxs > 0){
15227                 labelCfg.cls += ' col-xs-' + this.labelxs;
15228                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15229             }
15230                 
15231                 
15232         } else if ( this.fieldLabel.length) {
15233 //                Roo.log(" label");
15234                  cfg.cn = [
15235                    indicator,
15236                     {
15237                         tag: 'label',
15238                         //cls : 'input-group-addon',
15239                         html : this.fieldLabel
15240                     },
15241                     combobox
15242                 ];
15243                 
15244                 if(this.indicatorpos == 'right'){
15245                     cfg.cn = [
15246                         {
15247                             tag: 'label',
15248                             //cls : 'input-group-addon',
15249                             html : this.fieldLabel
15250                         },
15251                         indicator,
15252                         combobox
15253                     ];
15254                     
15255                 }
15256
15257         } else {
15258             
15259 //                Roo.log(" no label && no align");
15260                 cfg = combobox
15261                      
15262                 
15263         }
15264          
15265         var settings=this;
15266         ['xs','sm','md','lg'].map(function(size){
15267             if (settings[size]) {
15268                 cfg.cls += ' col-' + size + '-' + settings[size];
15269             }
15270         });
15271         
15272         return cfg;
15273         
15274     },
15275     
15276     _initEventsCalled : false,
15277     
15278     // private
15279     initEvents: function()
15280     {   
15281         if (this._initEventsCalled) { // as we call render... prevent looping...
15282             return;
15283         }
15284         this._initEventsCalled = true;
15285         
15286         if (!this.store) {
15287             throw "can not find store for combo";
15288         }
15289         
15290         this.indicator = this.indicatorEl();
15291         
15292         this.store = Roo.factory(this.store, Roo.data);
15293         this.store.parent = this;
15294         
15295         // if we are building from html. then this element is so complex, that we can not really
15296         // use the rendered HTML.
15297         // so we have to trash and replace the previous code.
15298         if (Roo.XComponent.build_from_html) {
15299             // remove this element....
15300             var e = this.el.dom, k=0;
15301             while (e ) { e = e.previousSibling;  ++k;}
15302
15303             this.el.remove();
15304             
15305             this.el=false;
15306             this.rendered = false;
15307             
15308             this.render(this.parent().getChildContainer(true), k);
15309         }
15310         
15311         if(Roo.isIOS && this.useNativeIOS){
15312             this.initIOSView();
15313             return;
15314         }
15315         
15316         /*
15317          * Touch Devices
15318          */
15319         
15320         if(Roo.isTouch && this.mobileTouchView){
15321             this.initTouchView();
15322             return;
15323         }
15324         
15325         if(this.tickable){
15326             this.initTickableEvents();
15327             return;
15328         }
15329         
15330         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15331         
15332         if(this.hiddenName){
15333             
15334             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15335             
15336             this.hiddenField.dom.value =
15337                 this.hiddenValue !== undefined ? this.hiddenValue :
15338                 this.value !== undefined ? this.value : '';
15339
15340             // prevent input submission
15341             this.el.dom.removeAttribute('name');
15342             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15343              
15344              
15345         }
15346         //if(Roo.isGecko){
15347         //    this.el.dom.setAttribute('autocomplete', 'off');
15348         //}
15349         
15350         var cls = 'x-combo-list';
15351         
15352         //this.list = new Roo.Layer({
15353         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15354         //});
15355         
15356         var _this = this;
15357         
15358         (function(){
15359             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15360             _this.list.setWidth(lw);
15361         }).defer(100);
15362         
15363         this.list.on('mouseover', this.onViewOver, this);
15364         this.list.on('mousemove', this.onViewMove, this);
15365         this.list.on('scroll', this.onViewScroll, this);
15366         
15367         /*
15368         this.list.swallowEvent('mousewheel');
15369         this.assetHeight = 0;
15370
15371         if(this.title){
15372             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15373             this.assetHeight += this.header.getHeight();
15374         }
15375
15376         this.innerList = this.list.createChild({cls:cls+'-inner'});
15377         this.innerList.on('mouseover', this.onViewOver, this);
15378         this.innerList.on('mousemove', this.onViewMove, this);
15379         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15380         
15381         if(this.allowBlank && !this.pageSize && !this.disableClear){
15382             this.footer = this.list.createChild({cls:cls+'-ft'});
15383             this.pageTb = new Roo.Toolbar(this.footer);
15384            
15385         }
15386         if(this.pageSize){
15387             this.footer = this.list.createChild({cls:cls+'-ft'});
15388             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15389                     {pageSize: this.pageSize});
15390             
15391         }
15392         
15393         if (this.pageTb && this.allowBlank && !this.disableClear) {
15394             var _this = this;
15395             this.pageTb.add(new Roo.Toolbar.Fill(), {
15396                 cls: 'x-btn-icon x-btn-clear',
15397                 text: '&#160;',
15398                 handler: function()
15399                 {
15400                     _this.collapse();
15401                     _this.clearValue();
15402                     _this.onSelect(false, -1);
15403                 }
15404             });
15405         }
15406         if (this.footer) {
15407             this.assetHeight += this.footer.getHeight();
15408         }
15409         */
15410             
15411         if(!this.tpl){
15412             this.tpl = Roo.bootstrap.version == 4 ?
15413                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15414                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15415         }
15416
15417         this.view = new Roo.View(this.list, this.tpl, {
15418             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15419         });
15420         //this.view.wrapEl.setDisplayed(false);
15421         this.view.on('click', this.onViewClick, this);
15422         
15423         
15424         this.store.on('beforeload', this.onBeforeLoad, this);
15425         this.store.on('load', this.onLoad, this);
15426         this.store.on('loadexception', this.onLoadException, this);
15427         /*
15428         if(this.resizable){
15429             this.resizer = new Roo.Resizable(this.list,  {
15430                pinned:true, handles:'se'
15431             });
15432             this.resizer.on('resize', function(r, w, h){
15433                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15434                 this.listWidth = w;
15435                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15436                 this.restrictHeight();
15437             }, this);
15438             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15439         }
15440         */
15441         if(!this.editable){
15442             this.editable = true;
15443             this.setEditable(false);
15444         }
15445         
15446         /*
15447         
15448         if (typeof(this.events.add.listeners) != 'undefined') {
15449             
15450             this.addicon = this.wrap.createChild(
15451                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15452        
15453             this.addicon.on('click', function(e) {
15454                 this.fireEvent('add', this);
15455             }, this);
15456         }
15457         if (typeof(this.events.edit.listeners) != 'undefined') {
15458             
15459             this.editicon = this.wrap.createChild(
15460                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15461             if (this.addicon) {
15462                 this.editicon.setStyle('margin-left', '40px');
15463             }
15464             this.editicon.on('click', function(e) {
15465                 
15466                 // we fire even  if inothing is selected..
15467                 this.fireEvent('edit', this, this.lastData );
15468                 
15469             }, this);
15470         }
15471         */
15472         
15473         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15474             "up" : function(e){
15475                 this.inKeyMode = true;
15476                 this.selectPrev();
15477             },
15478
15479             "down" : function(e){
15480                 if(!this.isExpanded()){
15481                     this.onTriggerClick();
15482                 }else{
15483                     this.inKeyMode = true;
15484                     this.selectNext();
15485                 }
15486             },
15487
15488             "enter" : function(e){
15489 //                this.onViewClick();
15490                 //return true;
15491                 this.collapse();
15492                 
15493                 if(this.fireEvent("specialkey", this, e)){
15494                     this.onViewClick(false);
15495                 }
15496                 
15497                 return true;
15498             },
15499
15500             "esc" : function(e){
15501                 this.collapse();
15502             },
15503
15504             "tab" : function(e){
15505                 this.collapse();
15506                 
15507                 if(this.fireEvent("specialkey", this, e)){
15508                     this.onViewClick(false);
15509                 }
15510                 
15511                 return true;
15512             },
15513
15514             scope : this,
15515
15516             doRelay : function(foo, bar, hname){
15517                 if(hname == 'down' || this.scope.isExpanded()){
15518                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15519                 }
15520                 return true;
15521             },
15522
15523             forceKeyDown: true
15524         });
15525         
15526         
15527         this.queryDelay = Math.max(this.queryDelay || 10,
15528                 this.mode == 'local' ? 10 : 250);
15529         
15530         
15531         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15532         
15533         if(this.typeAhead){
15534             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15535         }
15536         if(this.editable !== false){
15537             this.inputEl().on("keyup", this.onKeyUp, this);
15538         }
15539         if(this.forceSelection){
15540             this.inputEl().on('blur', this.doForce, this);
15541         }
15542         
15543         if(this.multiple){
15544             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15545             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15546         }
15547     },
15548     
15549     initTickableEvents: function()
15550     {   
15551         this.createList();
15552         
15553         if(this.hiddenName){
15554             
15555             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15556             
15557             this.hiddenField.dom.value =
15558                 this.hiddenValue !== undefined ? this.hiddenValue :
15559                 this.value !== undefined ? this.value : '';
15560
15561             // prevent input submission
15562             this.el.dom.removeAttribute('name');
15563             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15564              
15565              
15566         }
15567         
15568 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15569         
15570         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15571         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15572         if(this.triggerList){
15573             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15574         }
15575          
15576         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15577         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15578         
15579         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15580         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15581         
15582         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15583         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15584         
15585         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15586         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15587         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15588         
15589         this.okBtn.hide();
15590         this.cancelBtn.hide();
15591         
15592         var _this = this;
15593         
15594         (function(){
15595             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15596             _this.list.setWidth(lw);
15597         }).defer(100);
15598         
15599         this.list.on('mouseover', this.onViewOver, this);
15600         this.list.on('mousemove', this.onViewMove, this);
15601         
15602         this.list.on('scroll', this.onViewScroll, this);
15603         
15604         if(!this.tpl){
15605             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15606                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15607         }
15608
15609         this.view = new Roo.View(this.list, this.tpl, {
15610             singleSelect:true,
15611             tickable:true,
15612             parent:this,
15613             store: this.store,
15614             selectedClass: this.selectedClass
15615         });
15616         
15617         //this.view.wrapEl.setDisplayed(false);
15618         this.view.on('click', this.onViewClick, this);
15619         
15620         
15621         
15622         this.store.on('beforeload', this.onBeforeLoad, this);
15623         this.store.on('load', this.onLoad, this);
15624         this.store.on('loadexception', this.onLoadException, this);
15625         
15626         if(this.editable){
15627             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15628                 "up" : function(e){
15629                     this.inKeyMode = true;
15630                     this.selectPrev();
15631                 },
15632
15633                 "down" : function(e){
15634                     this.inKeyMode = true;
15635                     this.selectNext();
15636                 },
15637
15638                 "enter" : function(e){
15639                     if(this.fireEvent("specialkey", this, e)){
15640                         this.onViewClick(false);
15641                     }
15642                     
15643                     return true;
15644                 },
15645
15646                 "esc" : function(e){
15647                     this.onTickableFooterButtonClick(e, false, false);
15648                 },
15649
15650                 "tab" : function(e){
15651                     this.fireEvent("specialkey", this, e);
15652                     
15653                     this.onTickableFooterButtonClick(e, false, false);
15654                     
15655                     return true;
15656                 },
15657
15658                 scope : this,
15659
15660                 doRelay : function(e, fn, key){
15661                     if(this.scope.isExpanded()){
15662                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15663                     }
15664                     return true;
15665                 },
15666
15667                 forceKeyDown: true
15668             });
15669         }
15670         
15671         this.queryDelay = Math.max(this.queryDelay || 10,
15672                 this.mode == 'local' ? 10 : 250);
15673         
15674         
15675         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15676         
15677         if(this.typeAhead){
15678             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15679         }
15680         
15681         if(this.editable !== false){
15682             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15683         }
15684         
15685         this.indicator = this.indicatorEl();
15686         
15687         if(this.indicator){
15688             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15689             this.indicator.hide();
15690         }
15691         
15692     },
15693
15694     onDestroy : function(){
15695         if(this.view){
15696             this.view.setStore(null);
15697             this.view.el.removeAllListeners();
15698             this.view.el.remove();
15699             this.view.purgeListeners();
15700         }
15701         if(this.list){
15702             this.list.dom.innerHTML  = '';
15703         }
15704         
15705         if(this.store){
15706             this.store.un('beforeload', this.onBeforeLoad, this);
15707             this.store.un('load', this.onLoad, this);
15708             this.store.un('loadexception', this.onLoadException, this);
15709         }
15710         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15711     },
15712
15713     // private
15714     fireKey : function(e){
15715         if(e.isNavKeyPress() && !this.list.isVisible()){
15716             this.fireEvent("specialkey", this, e);
15717         }
15718     },
15719
15720     // private
15721     onResize: function(w, h){
15722 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15723 //        
15724 //        if(typeof w != 'number'){
15725 //            // we do not handle it!?!?
15726 //            return;
15727 //        }
15728 //        var tw = this.trigger.getWidth();
15729 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15730 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15731 //        var x = w - tw;
15732 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15733 //            
15734 //        //this.trigger.setStyle('left', x+'px');
15735 //        
15736 //        if(this.list && this.listWidth === undefined){
15737 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15738 //            this.list.setWidth(lw);
15739 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15740 //        }
15741         
15742     
15743         
15744     },
15745
15746     /**
15747      * Allow or prevent the user from directly editing the field text.  If false is passed,
15748      * the user will only be able to select from the items defined in the dropdown list.  This method
15749      * is the runtime equivalent of setting the 'editable' config option at config time.
15750      * @param {Boolean} value True to allow the user to directly edit the field text
15751      */
15752     setEditable : function(value){
15753         if(value == this.editable){
15754             return;
15755         }
15756         this.editable = value;
15757         if(!value){
15758             this.inputEl().dom.setAttribute('readOnly', true);
15759             this.inputEl().on('mousedown', this.onTriggerClick,  this);
15760             this.inputEl().addClass('x-combo-noedit');
15761         }else{
15762             this.inputEl().dom.setAttribute('readOnly', false);
15763             this.inputEl().un('mousedown', this.onTriggerClick,  this);
15764             this.inputEl().removeClass('x-combo-noedit');
15765         }
15766     },
15767
15768     // private
15769     
15770     onBeforeLoad : function(combo,opts){
15771         if(!this.hasFocus){
15772             return;
15773         }
15774          if (!opts.add) {
15775             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15776          }
15777         this.restrictHeight();
15778         this.selectedIndex = -1;
15779     },
15780
15781     // private
15782     onLoad : function(){
15783         
15784         this.hasQuery = false;
15785         
15786         if(!this.hasFocus){
15787             return;
15788         }
15789         
15790         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15791             this.loading.hide();
15792         }
15793         
15794         if(this.store.getCount() > 0){
15795             
15796             this.expand();
15797             this.restrictHeight();
15798             if(this.lastQuery == this.allQuery){
15799                 if(this.editable && !this.tickable){
15800                     this.inputEl().dom.select();
15801                 }
15802                 
15803                 if(
15804                     !this.selectByValue(this.value, true) &&
15805                     this.autoFocus && 
15806                     (
15807                         !this.store.lastOptions ||
15808                         typeof(this.store.lastOptions.add) == 'undefined' || 
15809                         this.store.lastOptions.add != true
15810                     )
15811                 ){
15812                     this.select(0, true);
15813                 }
15814             }else{
15815                 if(this.autoFocus){
15816                     this.selectNext();
15817                 }
15818                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15819                     this.taTask.delay(this.typeAheadDelay);
15820                 }
15821             }
15822         }else{
15823             this.onEmptyResults();
15824         }
15825         
15826         //this.el.focus();
15827     },
15828     // private
15829     onLoadException : function()
15830     {
15831         this.hasQuery = false;
15832         
15833         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15834             this.loading.hide();
15835         }
15836         
15837         if(this.tickable && this.editable){
15838             return;
15839         }
15840         
15841         this.collapse();
15842         // only causes errors at present
15843         //Roo.log(this.store.reader.jsonData);
15844         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15845             // fixme
15846             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15847         //}
15848         
15849         
15850     },
15851     // private
15852     onTypeAhead : function(){
15853         if(this.store.getCount() > 0){
15854             var r = this.store.getAt(0);
15855             var newValue = r.data[this.displayField];
15856             var len = newValue.length;
15857             var selStart = this.getRawValue().length;
15858             
15859             if(selStart != len){
15860                 this.setRawValue(newValue);
15861                 this.selectText(selStart, newValue.length);
15862             }
15863         }
15864     },
15865
15866     // private
15867     onSelect : function(record, index){
15868         
15869         if(this.fireEvent('beforeselect', this, record, index) !== false){
15870         
15871             this.setFromData(index > -1 ? record.data : false);
15872             
15873             this.collapse();
15874             this.fireEvent('select', this, record, index);
15875         }
15876     },
15877
15878     /**
15879      * Returns the currently selected field value or empty string if no value is set.
15880      * @return {String} value The selected value
15881      */
15882     getValue : function()
15883     {
15884         if(Roo.isIOS && this.useNativeIOS){
15885             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15886         }
15887         
15888         if(this.multiple){
15889             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
15890         }
15891         
15892         if(this.valueField){
15893             return typeof this.value != 'undefined' ? this.value : '';
15894         }else{
15895             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
15896         }
15897     },
15898     
15899     getRawValue : function()
15900     {
15901         if(Roo.isIOS && this.useNativeIOS){
15902             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
15903         }
15904         
15905         var v = this.inputEl().getValue();
15906         
15907         return v;
15908     },
15909
15910     /**
15911      * Clears any text/value currently set in the field
15912      */
15913     clearValue : function(){
15914         
15915         if(this.hiddenField){
15916             this.hiddenField.dom.value = '';
15917         }
15918         this.value = '';
15919         this.setRawValue('');
15920         this.lastSelectionText = '';
15921         this.lastData = false;
15922         
15923         var close = this.closeTriggerEl();
15924         
15925         if(close){
15926             close.hide();
15927         }
15928         
15929         this.validate();
15930         
15931     },
15932
15933     /**
15934      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
15935      * will be displayed in the field.  If the value does not match the data value of an existing item,
15936      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
15937      * Otherwise the field will be blank (although the value will still be set).
15938      * @param {String} value The value to match
15939      */
15940     setValue : function(v)
15941     {
15942         if(Roo.isIOS && this.useNativeIOS){
15943             this.setIOSValue(v);
15944             return;
15945         }
15946         
15947         if(this.multiple){
15948             this.syncValue();
15949             return;
15950         }
15951         
15952         var text = v;
15953         if(this.valueField){
15954             var r = this.findRecord(this.valueField, v);
15955             if(r){
15956                 text = r.data[this.displayField];
15957             }else if(this.valueNotFoundText !== undefined){
15958                 text = this.valueNotFoundText;
15959             }
15960         }
15961         this.lastSelectionText = text;
15962         if(this.hiddenField){
15963             this.hiddenField.dom.value = v;
15964         }
15965         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
15966         this.value = v;
15967         
15968         var close = this.closeTriggerEl();
15969         
15970         if(close){
15971             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
15972         }
15973         
15974         this.validate();
15975     },
15976     /**
15977      * @property {Object} the last set data for the element
15978      */
15979     
15980     lastData : false,
15981     /**
15982      * Sets the value of the field based on a object which is related to the record format for the store.
15983      * @param {Object} value the value to set as. or false on reset?
15984      */
15985     setFromData : function(o){
15986         
15987         if(this.multiple){
15988             this.addItem(o);
15989             return;
15990         }
15991             
15992         var dv = ''; // display value
15993         var vv = ''; // value value..
15994         this.lastData = o;
15995         if (this.displayField) {
15996             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15997         } else {
15998             // this is an error condition!!!
15999             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16000         }
16001         
16002         if(this.valueField){
16003             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16004         }
16005         
16006         var close = this.closeTriggerEl();
16007         
16008         if(close){
16009             if(dv.length || vv * 1 > 0){
16010                 close.show() ;
16011                 this.blockFocus=true;
16012             } else {
16013                 close.hide();
16014             }             
16015         }
16016         
16017         if(this.hiddenField){
16018             this.hiddenField.dom.value = vv;
16019             
16020             this.lastSelectionText = dv;
16021             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16022             this.value = vv;
16023             return;
16024         }
16025         // no hidden field.. - we store the value in 'value', but still display
16026         // display field!!!!
16027         this.lastSelectionText = dv;
16028         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16029         this.value = vv;
16030         
16031         
16032         
16033     },
16034     // private
16035     reset : function(){
16036         // overridden so that last data is reset..
16037         
16038         if(this.multiple){
16039             this.clearItem();
16040             return;
16041         }
16042         
16043         this.setValue(this.originalValue);
16044         //this.clearInvalid();
16045         this.lastData = false;
16046         if (this.view) {
16047             this.view.clearSelections();
16048         }
16049         
16050         this.validate();
16051     },
16052     // private
16053     findRecord : function(prop, value){
16054         var record;
16055         if(this.store.getCount() > 0){
16056             this.store.each(function(r){
16057                 if(r.data[prop] == value){
16058                     record = r;
16059                     return false;
16060                 }
16061                 return true;
16062             });
16063         }
16064         return record;
16065     },
16066     
16067     getName: function()
16068     {
16069         // returns hidden if it's set..
16070         if (!this.rendered) {return ''};
16071         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16072         
16073     },
16074     // private
16075     onViewMove : function(e, t){
16076         this.inKeyMode = false;
16077     },
16078
16079     // private
16080     onViewOver : function(e, t){
16081         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16082             return;
16083         }
16084         var item = this.view.findItemFromChild(t);
16085         
16086         if(item){
16087             var index = this.view.indexOf(item);
16088             this.select(index, false);
16089         }
16090     },
16091
16092     // private
16093     onViewClick : function(view, doFocus, el, e)
16094     {
16095         var index = this.view.getSelectedIndexes()[0];
16096         
16097         var r = this.store.getAt(index);
16098         
16099         if(this.tickable){
16100             
16101             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16102                 return;
16103             }
16104             
16105             var rm = false;
16106             var _this = this;
16107             
16108             Roo.each(this.tickItems, function(v,k){
16109                 
16110                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16111                     Roo.log(v);
16112                     _this.tickItems.splice(k, 1);
16113                     
16114                     if(typeof(e) == 'undefined' && view == false){
16115                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16116                     }
16117                     
16118                     rm = true;
16119                     return;
16120                 }
16121             });
16122             
16123             if(rm){
16124                 return;
16125             }
16126             
16127             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16128                 this.tickItems.push(r.data);
16129             }
16130             
16131             if(typeof(e) == 'undefined' && view == false){
16132                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16133             }
16134                     
16135             return;
16136         }
16137         
16138         if(r){
16139             this.onSelect(r, index);
16140         }
16141         if(doFocus !== false && !this.blockFocus){
16142             this.inputEl().focus();
16143         }
16144     },
16145
16146     // private
16147     restrictHeight : function(){
16148         //this.innerList.dom.style.height = '';
16149         //var inner = this.innerList.dom;
16150         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16151         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16152         //this.list.beginUpdate();
16153         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16154         this.list.alignTo(this.inputEl(), this.listAlign);
16155         this.list.alignTo(this.inputEl(), this.listAlign);
16156         //this.list.endUpdate();
16157     },
16158
16159     // private
16160     onEmptyResults : function(){
16161         
16162         if(this.tickable && this.editable){
16163             this.hasFocus = false;
16164             this.restrictHeight();
16165             return;
16166         }
16167         
16168         this.collapse();
16169     },
16170
16171     /**
16172      * Returns true if the dropdown list is expanded, else false.
16173      */
16174     isExpanded : function(){
16175         return this.list.isVisible();
16176     },
16177
16178     /**
16179      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16180      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16181      * @param {String} value The data value of the item to select
16182      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16183      * selected item if it is not currently in view (defaults to true)
16184      * @return {Boolean} True if the value matched an item in the list, else false
16185      */
16186     selectByValue : function(v, scrollIntoView){
16187         if(v !== undefined && v !== null){
16188             var r = this.findRecord(this.valueField || this.displayField, v);
16189             if(r){
16190                 this.select(this.store.indexOf(r), scrollIntoView);
16191                 return true;
16192             }
16193         }
16194         return false;
16195     },
16196
16197     /**
16198      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16199      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16200      * @param {Number} index The zero-based index of the list item to select
16201      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16202      * selected item if it is not currently in view (defaults to true)
16203      */
16204     select : function(index, scrollIntoView){
16205         this.selectedIndex = index;
16206         this.view.select(index);
16207         if(scrollIntoView !== false){
16208             var el = this.view.getNode(index);
16209             /*
16210              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16211              */
16212             if(el){
16213                 this.list.scrollChildIntoView(el, false);
16214             }
16215         }
16216     },
16217
16218     // private
16219     selectNext : function(){
16220         var ct = this.store.getCount();
16221         if(ct > 0){
16222             if(this.selectedIndex == -1){
16223                 this.select(0);
16224             }else if(this.selectedIndex < ct-1){
16225                 this.select(this.selectedIndex+1);
16226             }
16227         }
16228     },
16229
16230     // private
16231     selectPrev : function(){
16232         var ct = this.store.getCount();
16233         if(ct > 0){
16234             if(this.selectedIndex == -1){
16235                 this.select(0);
16236             }else if(this.selectedIndex != 0){
16237                 this.select(this.selectedIndex-1);
16238             }
16239         }
16240     },
16241
16242     // private
16243     onKeyUp : function(e){
16244         if(this.editable !== false && !e.isSpecialKey()){
16245             this.lastKey = e.getKey();
16246             this.dqTask.delay(this.queryDelay);
16247         }
16248     },
16249
16250     // private
16251     validateBlur : function(){
16252         return !this.list || !this.list.isVisible();   
16253     },
16254
16255     // private
16256     initQuery : function(){
16257         
16258         var v = this.getRawValue();
16259         
16260         if(this.tickable && this.editable){
16261             v = this.tickableInputEl().getValue();
16262         }
16263         
16264         this.doQuery(v);
16265     },
16266
16267     // private
16268     doForce : function(){
16269         if(this.inputEl().dom.value.length > 0){
16270             this.inputEl().dom.value =
16271                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16272              
16273         }
16274     },
16275
16276     /**
16277      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16278      * query allowing the query action to be canceled if needed.
16279      * @param {String} query The SQL query to execute
16280      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16281      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16282      * saved in the current store (defaults to false)
16283      */
16284     doQuery : function(q, forceAll){
16285         
16286         if(q === undefined || q === null){
16287             q = '';
16288         }
16289         var qe = {
16290             query: q,
16291             forceAll: forceAll,
16292             combo: this,
16293             cancel:false
16294         };
16295         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16296             return false;
16297         }
16298         q = qe.query;
16299         
16300         forceAll = qe.forceAll;
16301         if(forceAll === true || (q.length >= this.minChars)){
16302             
16303             this.hasQuery = true;
16304             
16305             if(this.lastQuery != q || this.alwaysQuery){
16306                 this.lastQuery = q;
16307                 if(this.mode == 'local'){
16308                     this.selectedIndex = -1;
16309                     if(forceAll){
16310                         this.store.clearFilter();
16311                     }else{
16312                         
16313                         if(this.specialFilter){
16314                             this.fireEvent('specialfilter', this);
16315                             this.onLoad();
16316                             return;
16317                         }
16318                         
16319                         this.store.filter(this.displayField, q);
16320                     }
16321                     
16322                     this.store.fireEvent("datachanged", this.store);
16323                     
16324                     this.onLoad();
16325                     
16326                     
16327                 }else{
16328                     
16329                     this.store.baseParams[this.queryParam] = q;
16330                     
16331                     var options = {params : this.getParams(q)};
16332                     
16333                     if(this.loadNext){
16334                         options.add = true;
16335                         options.params.start = this.page * this.pageSize;
16336                     }
16337                     
16338                     this.store.load(options);
16339                     
16340                     /*
16341                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16342                      *  we should expand the list on onLoad
16343                      *  so command out it
16344                      */
16345 //                    this.expand();
16346                 }
16347             }else{
16348                 this.selectedIndex = -1;
16349                 this.onLoad();   
16350             }
16351         }
16352         
16353         this.loadNext = false;
16354     },
16355     
16356     // private
16357     getParams : function(q){
16358         var p = {};
16359         //p[this.queryParam] = q;
16360         
16361         if(this.pageSize){
16362             p.start = 0;
16363             p.limit = this.pageSize;
16364         }
16365         return p;
16366     },
16367
16368     /**
16369      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16370      */
16371     collapse : function(){
16372         if(!this.isExpanded()){
16373             return;
16374         }
16375         
16376         this.list.hide();
16377         
16378         this.hasFocus = false;
16379         
16380         if(this.tickable){
16381             this.okBtn.hide();
16382             this.cancelBtn.hide();
16383             this.trigger.show();
16384             
16385             if(this.editable){
16386                 this.tickableInputEl().dom.value = '';
16387                 this.tickableInputEl().blur();
16388             }
16389             
16390         }
16391         
16392         Roo.get(document).un('mousedown', this.collapseIf, this);
16393         Roo.get(document).un('mousewheel', this.collapseIf, this);
16394         if (!this.editable) {
16395             Roo.get(document).un('keydown', this.listKeyPress, this);
16396         }
16397         this.fireEvent('collapse', this);
16398         
16399         this.validate();
16400     },
16401
16402     // private
16403     collapseIf : function(e){
16404         var in_combo  = e.within(this.el);
16405         var in_list =  e.within(this.list);
16406         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16407         
16408         if (in_combo || in_list || is_list) {
16409             //e.stopPropagation();
16410             return;
16411         }
16412         
16413         if(this.tickable){
16414             this.onTickableFooterButtonClick(e, false, false);
16415         }
16416
16417         this.collapse();
16418         
16419     },
16420
16421     /**
16422      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16423      */
16424     expand : function(){
16425        
16426         if(this.isExpanded() || !this.hasFocus){
16427             return;
16428         }
16429         
16430         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16431         this.list.setWidth(lw);
16432         
16433         Roo.log('expand');
16434         
16435         this.list.show();
16436         
16437         this.restrictHeight();
16438         
16439         if(this.tickable){
16440             
16441             this.tickItems = Roo.apply([], this.item);
16442             
16443             this.okBtn.show();
16444             this.cancelBtn.show();
16445             this.trigger.hide();
16446             
16447             if(this.editable){
16448                 this.tickableInputEl().focus();
16449             }
16450             
16451         }
16452         
16453         Roo.get(document).on('mousedown', this.collapseIf, this);
16454         Roo.get(document).on('mousewheel', this.collapseIf, this);
16455         if (!this.editable) {
16456             Roo.get(document).on('keydown', this.listKeyPress, this);
16457         }
16458         
16459         this.fireEvent('expand', this);
16460     },
16461
16462     // private
16463     // Implements the default empty TriggerField.onTriggerClick function
16464     onTriggerClick : function(e)
16465     {
16466         Roo.log('trigger click');
16467         
16468         if(this.disabled || !this.triggerList){
16469             return;
16470         }
16471         
16472         this.page = 0;
16473         this.loadNext = false;
16474         
16475         if(this.isExpanded()){
16476             this.collapse();
16477             if (!this.blockFocus) {
16478                 this.inputEl().focus();
16479             }
16480             
16481         }else {
16482             this.hasFocus = true;
16483             if(this.triggerAction == 'all') {
16484                 this.doQuery(this.allQuery, true);
16485             } else {
16486                 this.doQuery(this.getRawValue());
16487             }
16488             if (!this.blockFocus) {
16489                 this.inputEl().focus();
16490             }
16491         }
16492     },
16493     
16494     onTickableTriggerClick : function(e)
16495     {
16496         if(this.disabled){
16497             return;
16498         }
16499         
16500         this.page = 0;
16501         this.loadNext = false;
16502         this.hasFocus = true;
16503         
16504         if(this.triggerAction == 'all') {
16505             this.doQuery(this.allQuery, true);
16506         } else {
16507             this.doQuery(this.getRawValue());
16508         }
16509     },
16510     
16511     onSearchFieldClick : function(e)
16512     {
16513         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16514             this.onTickableFooterButtonClick(e, false, false);
16515             return;
16516         }
16517         
16518         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16519             return;
16520         }
16521         
16522         this.page = 0;
16523         this.loadNext = false;
16524         this.hasFocus = true;
16525         
16526         if(this.triggerAction == 'all') {
16527             this.doQuery(this.allQuery, true);
16528         } else {
16529             this.doQuery(this.getRawValue());
16530         }
16531     },
16532     
16533     listKeyPress : function(e)
16534     {
16535         //Roo.log('listkeypress');
16536         // scroll to first matching element based on key pres..
16537         if (e.isSpecialKey()) {
16538             return false;
16539         }
16540         var k = String.fromCharCode(e.getKey()).toUpperCase();
16541         //Roo.log(k);
16542         var match  = false;
16543         var csel = this.view.getSelectedNodes();
16544         var cselitem = false;
16545         if (csel.length) {
16546             var ix = this.view.indexOf(csel[0]);
16547             cselitem  = this.store.getAt(ix);
16548             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16549                 cselitem = false;
16550             }
16551             
16552         }
16553         
16554         this.store.each(function(v) { 
16555             if (cselitem) {
16556                 // start at existing selection.
16557                 if (cselitem.id == v.id) {
16558                     cselitem = false;
16559                 }
16560                 return true;
16561             }
16562                 
16563             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16564                 match = this.store.indexOf(v);
16565                 return false;
16566             }
16567             return true;
16568         }, this);
16569         
16570         if (match === false) {
16571             return true; // no more action?
16572         }
16573         // scroll to?
16574         this.view.select(match);
16575         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16576         sn.scrollIntoView(sn.dom.parentNode, false);
16577     },
16578     
16579     onViewScroll : function(e, t){
16580         
16581         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){
16582             return;
16583         }
16584         
16585         this.hasQuery = true;
16586         
16587         this.loading = this.list.select('.loading', true).first();
16588         
16589         if(this.loading === null){
16590             this.list.createChild({
16591                 tag: 'div',
16592                 cls: 'loading roo-select2-more-results roo-select2-active',
16593                 html: 'Loading more results...'
16594             });
16595             
16596             this.loading = this.list.select('.loading', true).first();
16597             
16598             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16599             
16600             this.loading.hide();
16601         }
16602         
16603         this.loading.show();
16604         
16605         var _combo = this;
16606         
16607         this.page++;
16608         this.loadNext = true;
16609         
16610         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16611         
16612         return;
16613     },
16614     
16615     addItem : function(o)
16616     {   
16617         var dv = ''; // display value
16618         
16619         if (this.displayField) {
16620             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16621         } else {
16622             // this is an error condition!!!
16623             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16624         }
16625         
16626         if(!dv.length){
16627             return;
16628         }
16629         
16630         var choice = this.choices.createChild({
16631             tag: 'li',
16632             cls: 'roo-select2-search-choice',
16633             cn: [
16634                 {
16635                     tag: 'div',
16636                     html: dv
16637                 },
16638                 {
16639                     tag: 'a',
16640                     href: '#',
16641                     cls: 'roo-select2-search-choice-close fa fa-times',
16642                     tabindex: '-1'
16643                 }
16644             ]
16645             
16646         }, this.searchField);
16647         
16648         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16649         
16650         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16651         
16652         this.item.push(o);
16653         
16654         this.lastData = o;
16655         
16656         this.syncValue();
16657         
16658         this.inputEl().dom.value = '';
16659         
16660         this.validate();
16661     },
16662     
16663     onRemoveItem : function(e, _self, o)
16664     {
16665         e.preventDefault();
16666         
16667         this.lastItem = Roo.apply([], this.item);
16668         
16669         var index = this.item.indexOf(o.data) * 1;
16670         
16671         if( index < 0){
16672             Roo.log('not this item?!');
16673             return;
16674         }
16675         
16676         this.item.splice(index, 1);
16677         o.item.remove();
16678         
16679         this.syncValue();
16680         
16681         this.fireEvent('remove', this, e);
16682         
16683         this.validate();
16684         
16685     },
16686     
16687     syncValue : function()
16688     {
16689         if(!this.item.length){
16690             this.clearValue();
16691             return;
16692         }
16693             
16694         var value = [];
16695         var _this = this;
16696         Roo.each(this.item, function(i){
16697             if(_this.valueField){
16698                 value.push(i[_this.valueField]);
16699                 return;
16700             }
16701
16702             value.push(i);
16703         });
16704
16705         this.value = value.join(',');
16706
16707         if(this.hiddenField){
16708             this.hiddenField.dom.value = this.value;
16709         }
16710         
16711         this.store.fireEvent("datachanged", this.store);
16712         
16713         this.validate();
16714     },
16715     
16716     clearItem : function()
16717     {
16718         if(!this.multiple){
16719             return;
16720         }
16721         
16722         this.item = [];
16723         
16724         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16725            c.remove();
16726         });
16727         
16728         this.syncValue();
16729         
16730         this.validate();
16731         
16732         if(this.tickable && !Roo.isTouch){
16733             this.view.refresh();
16734         }
16735     },
16736     
16737     inputEl: function ()
16738     {
16739         if(Roo.isIOS && this.useNativeIOS){
16740             return this.el.select('select.roo-ios-select', true).first();
16741         }
16742         
16743         if(Roo.isTouch && this.mobileTouchView){
16744             return this.el.select('input.form-control',true).first();
16745         }
16746         
16747         if(this.tickable){
16748             return this.searchField;
16749         }
16750         
16751         return this.el.select('input.form-control',true).first();
16752     },
16753     
16754     onTickableFooterButtonClick : function(e, btn, el)
16755     {
16756         e.preventDefault();
16757         
16758         this.lastItem = Roo.apply([], this.item);
16759         
16760         if(btn && btn.name == 'cancel'){
16761             this.tickItems = Roo.apply([], this.item);
16762             this.collapse();
16763             return;
16764         }
16765         
16766         this.clearItem();
16767         
16768         var _this = this;
16769         
16770         Roo.each(this.tickItems, function(o){
16771             _this.addItem(o);
16772         });
16773         
16774         this.collapse();
16775         
16776     },
16777     
16778     validate : function()
16779     {
16780         if(this.getVisibilityEl().hasClass('hidden')){
16781             return true;
16782         }
16783         
16784         var v = this.getRawValue();
16785         
16786         if(this.multiple){
16787             v = this.getValue();
16788         }
16789         
16790         if(this.disabled || this.allowBlank || v.length){
16791             this.markValid();
16792             return true;
16793         }
16794         
16795         this.markInvalid();
16796         return false;
16797     },
16798     
16799     tickableInputEl : function()
16800     {
16801         if(!this.tickable || !this.editable){
16802             return this.inputEl();
16803         }
16804         
16805         return this.inputEl().select('.roo-select2-search-field-input', true).first();
16806     },
16807     
16808     
16809     getAutoCreateTouchView : function()
16810     {
16811         var id = Roo.id();
16812         
16813         var cfg = {
16814             cls: 'form-group' //input-group
16815         };
16816         
16817         var input =  {
16818             tag: 'input',
16819             id : id,
16820             type : this.inputType,
16821             cls : 'form-control x-combo-noedit',
16822             autocomplete: 'new-password',
16823             placeholder : this.placeholder || '',
16824             readonly : true
16825         };
16826         
16827         if (this.name) {
16828             input.name = this.name;
16829         }
16830         
16831         if (this.size) {
16832             input.cls += ' input-' + this.size;
16833         }
16834         
16835         if (this.disabled) {
16836             input.disabled = true;
16837         }
16838         
16839         var inputblock = {
16840             cls : '',
16841             cn : [
16842                 input
16843             ]
16844         };
16845         
16846         if(this.before){
16847             inputblock.cls += ' input-group';
16848             
16849             inputblock.cn.unshift({
16850                 tag :'span',
16851                 cls : 'input-group-addon input-group-prepend input-group-text',
16852                 html : this.before
16853             });
16854         }
16855         
16856         if(this.removable && !this.multiple){
16857             inputblock.cls += ' roo-removable';
16858             
16859             inputblock.cn.push({
16860                 tag: 'button',
16861                 html : 'x',
16862                 cls : 'roo-combo-removable-btn close'
16863             });
16864         }
16865
16866         if(this.hasFeedback && !this.allowBlank){
16867             
16868             inputblock.cls += ' has-feedback';
16869             
16870             inputblock.cn.push({
16871                 tag: 'span',
16872                 cls: 'glyphicon form-control-feedback'
16873             });
16874             
16875         }
16876         
16877         if (this.after) {
16878             
16879             inputblock.cls += (this.before) ? '' : ' input-group';
16880             
16881             inputblock.cn.push({
16882                 tag :'span',
16883                 cls : 'input-group-addon input-group-append input-group-text',
16884                 html : this.after
16885             });
16886         }
16887
16888         
16889         var ibwrap = inputblock;
16890         
16891         if(this.multiple){
16892             ibwrap = {
16893                 tag: 'ul',
16894                 cls: 'roo-select2-choices',
16895                 cn:[
16896                     {
16897                         tag: 'li',
16898                         cls: 'roo-select2-search-field',
16899                         cn: [
16900
16901                             inputblock
16902                         ]
16903                     }
16904                 ]
16905             };
16906         
16907             
16908         }
16909         
16910         var combobox = {
16911             cls: 'roo-select2-container input-group roo-touchview-combobox ',
16912             cn: [
16913                 {
16914                     tag: 'input',
16915                     type : 'hidden',
16916                     cls: 'form-hidden-field'
16917                 },
16918                 ibwrap
16919             ]
16920         };
16921         
16922         if(!this.multiple && this.showToggleBtn){
16923             
16924             var caret = {
16925                 cls: 'caret'
16926             };
16927             
16928             if (this.caret != false) {
16929                 caret = {
16930                      tag: 'i',
16931                      cls: 'fa fa-' + this.caret
16932                 };
16933                 
16934             }
16935             
16936             combobox.cn.push({
16937                 tag :'span',
16938                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
16939                 cn : [
16940                     Roo.bootstrap.version == 3 ? caret : '',
16941                     {
16942                         tag: 'span',
16943                         cls: 'combobox-clear',
16944                         cn  : [
16945                             {
16946                                 tag : 'i',
16947                                 cls: 'icon-remove'
16948                             }
16949                         ]
16950                     }
16951                 ]
16952
16953             })
16954         }
16955         
16956         if(this.multiple){
16957             combobox.cls += ' roo-select2-container-multi';
16958         }
16959         
16960         var align = this.labelAlign || this.parentLabelAlign();
16961         
16962         if (align ==='left' && this.fieldLabel.length) {
16963
16964             cfg.cn = [
16965                 {
16966                    tag : 'i',
16967                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
16968                    tooltip : 'This field is required'
16969                 },
16970                 {
16971                     tag: 'label',
16972                     cls : 'control-label col-form-label',
16973                     html : this.fieldLabel
16974
16975                 },
16976                 {
16977                     cls : '', 
16978                     cn: [
16979                         combobox
16980                     ]
16981                 }
16982             ];
16983             
16984             var labelCfg = cfg.cn[1];
16985             var contentCfg = cfg.cn[2];
16986             
16987
16988             if(this.indicatorpos == 'right'){
16989                 cfg.cn = [
16990                     {
16991                         tag: 'label',
16992                         'for' :  id,
16993                         cls : 'control-label col-form-label',
16994                         cn : [
16995                             {
16996                                 tag : 'span',
16997                                 html : this.fieldLabel
16998                             },
16999                             {
17000                                 tag : 'i',
17001                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17002                                 tooltip : 'This field is required'
17003                             }
17004                         ]
17005                     },
17006                     {
17007                         cls : "",
17008                         cn: [
17009                             combobox
17010                         ]
17011                     }
17012
17013                 ];
17014                 
17015                 labelCfg = cfg.cn[0];
17016                 contentCfg = cfg.cn[1];
17017             }
17018             
17019            
17020             
17021             if(this.labelWidth > 12){
17022                 labelCfg.style = "width: " + this.labelWidth + 'px';
17023             }
17024             
17025             if(this.labelWidth < 13 && this.labelmd == 0){
17026                 this.labelmd = this.labelWidth;
17027             }
17028             
17029             if(this.labellg > 0){
17030                 labelCfg.cls += ' col-lg-' + this.labellg;
17031                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17032             }
17033             
17034             if(this.labelmd > 0){
17035                 labelCfg.cls += ' col-md-' + this.labelmd;
17036                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17037             }
17038             
17039             if(this.labelsm > 0){
17040                 labelCfg.cls += ' col-sm-' + this.labelsm;
17041                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17042             }
17043             
17044             if(this.labelxs > 0){
17045                 labelCfg.cls += ' col-xs-' + this.labelxs;
17046                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17047             }
17048                 
17049                 
17050         } else if ( this.fieldLabel.length) {
17051             cfg.cn = [
17052                 {
17053                    tag : 'i',
17054                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17055                    tooltip : 'This field is required'
17056                 },
17057                 {
17058                     tag: 'label',
17059                     cls : 'control-label',
17060                     html : this.fieldLabel
17061
17062                 },
17063                 {
17064                     cls : '', 
17065                     cn: [
17066                         combobox
17067                     ]
17068                 }
17069             ];
17070             
17071             if(this.indicatorpos == 'right'){
17072                 cfg.cn = [
17073                     {
17074                         tag: 'label',
17075                         cls : 'control-label',
17076                         html : this.fieldLabel,
17077                         cn : [
17078                             {
17079                                tag : 'i',
17080                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17081                                tooltip : 'This field is required'
17082                             }
17083                         ]
17084                     },
17085                     {
17086                         cls : '', 
17087                         cn: [
17088                             combobox
17089                         ]
17090                     }
17091                 ];
17092             }
17093         } else {
17094             cfg.cn = combobox;    
17095         }
17096         
17097         
17098         var settings = this;
17099         
17100         ['xs','sm','md','lg'].map(function(size){
17101             if (settings[size]) {
17102                 cfg.cls += ' col-' + size + '-' + settings[size];
17103             }
17104         });
17105         
17106         return cfg;
17107     },
17108     
17109     initTouchView : function()
17110     {
17111         this.renderTouchView();
17112         
17113         this.touchViewEl.on('scroll', function(){
17114             this.el.dom.scrollTop = 0;
17115         }, this);
17116         
17117         this.originalValue = this.getValue();
17118         
17119         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17120         
17121         this.inputEl().on("click", this.showTouchView, this);
17122         if (this.triggerEl) {
17123             this.triggerEl.on("click", this.showTouchView, this);
17124         }
17125         
17126         
17127         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17128         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17129         
17130         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17131         
17132         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17133         this.store.on('load', this.onTouchViewLoad, this);
17134         this.store.on('loadexception', this.onTouchViewLoadException, this);
17135         
17136         if(this.hiddenName){
17137             
17138             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17139             
17140             this.hiddenField.dom.value =
17141                 this.hiddenValue !== undefined ? this.hiddenValue :
17142                 this.value !== undefined ? this.value : '';
17143         
17144             this.el.dom.removeAttribute('name');
17145             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17146         }
17147         
17148         if(this.multiple){
17149             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17150             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17151         }
17152         
17153         if(this.removable && !this.multiple){
17154             var close = this.closeTriggerEl();
17155             if(close){
17156                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17157                 close.on('click', this.removeBtnClick, this, close);
17158             }
17159         }
17160         /*
17161          * fix the bug in Safari iOS8
17162          */
17163         this.inputEl().on("focus", function(e){
17164             document.activeElement.blur();
17165         }, this);
17166         
17167         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17168         
17169         return;
17170         
17171         
17172     },
17173     
17174     renderTouchView : function()
17175     {
17176         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17177         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17178         
17179         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17180         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17181         
17182         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17183         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17184         this.touchViewBodyEl.setStyle('overflow', 'auto');
17185         
17186         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17187         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17188         
17189         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17190         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17191         
17192     },
17193     
17194     showTouchView : function()
17195     {
17196         if(this.disabled){
17197             return;
17198         }
17199         
17200         this.touchViewHeaderEl.hide();
17201
17202         if(this.modalTitle.length){
17203             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17204             this.touchViewHeaderEl.show();
17205         }
17206
17207         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17208         this.touchViewEl.show();
17209
17210         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17211         
17212         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17213         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17214
17215         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17216
17217         if(this.modalTitle.length){
17218             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17219         }
17220         
17221         this.touchViewBodyEl.setHeight(bodyHeight);
17222
17223         if(this.animate){
17224             var _this = this;
17225             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
17226         }else{
17227             this.touchViewEl.addClass('in');
17228         }
17229         
17230         if(this._touchViewMask){
17231             Roo.get(document.body).addClass("x-body-masked");
17232             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17233             this._touchViewMask.setStyle('z-index', 10000);
17234             this._touchViewMask.addClass('show');
17235         }
17236         
17237         this.doTouchViewQuery();
17238         
17239     },
17240     
17241     hideTouchView : function()
17242     {
17243         this.touchViewEl.removeClass('in');
17244
17245         if(this.animate){
17246             var _this = this;
17247             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17248         }else{
17249             this.touchViewEl.setStyle('display', 'none');
17250         }
17251         
17252         if(this._touchViewMask){
17253             this._touchViewMask.removeClass('show');
17254             Roo.get(document.body).removeClass("x-body-masked");
17255         }
17256     },
17257     
17258     setTouchViewValue : function()
17259     {
17260         if(this.multiple){
17261             this.clearItem();
17262         
17263             var _this = this;
17264
17265             Roo.each(this.tickItems, function(o){
17266                 this.addItem(o);
17267             }, this);
17268         }
17269         
17270         this.hideTouchView();
17271     },
17272     
17273     doTouchViewQuery : function()
17274     {
17275         var qe = {
17276             query: '',
17277             forceAll: true,
17278             combo: this,
17279             cancel:false
17280         };
17281         
17282         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17283             return false;
17284         }
17285         
17286         if(!this.alwaysQuery || this.mode == 'local'){
17287             this.onTouchViewLoad();
17288             return;
17289         }
17290         
17291         this.store.load();
17292     },
17293     
17294     onTouchViewBeforeLoad : function(combo,opts)
17295     {
17296         return;
17297     },
17298
17299     // private
17300     onTouchViewLoad : function()
17301     {
17302         if(this.store.getCount() < 1){
17303             this.onTouchViewEmptyResults();
17304             return;
17305         }
17306         
17307         this.clearTouchView();
17308         
17309         var rawValue = this.getRawValue();
17310         
17311         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17312         
17313         this.tickItems = [];
17314         
17315         this.store.data.each(function(d, rowIndex){
17316             var row = this.touchViewListGroup.createChild(template);
17317             
17318             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17319                 row.addClass(d.data.cls);
17320             }
17321             
17322             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17323                 var cfg = {
17324                     data : d.data,
17325                     html : d.data[this.displayField]
17326                 };
17327                 
17328                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17329                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17330                 }
17331             }
17332             row.removeClass('selected');
17333             if(!this.multiple && this.valueField &&
17334                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17335             {
17336                 // radio buttons..
17337                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17338                 row.addClass('selected');
17339             }
17340             
17341             if(this.multiple && this.valueField &&
17342                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17343             {
17344                 
17345                 // checkboxes...
17346                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17347                 this.tickItems.push(d.data);
17348             }
17349             
17350             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17351             
17352         }, this);
17353         
17354         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17355         
17356         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17357
17358         if(this.modalTitle.length){
17359             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17360         }
17361
17362         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17363         
17364         if(this.mobile_restrict_height && listHeight < bodyHeight){
17365             this.touchViewBodyEl.setHeight(listHeight);
17366         }
17367         
17368         var _this = this;
17369         
17370         if(firstChecked && listHeight > bodyHeight){
17371             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17372         }
17373         
17374     },
17375     
17376     onTouchViewLoadException : function()
17377     {
17378         this.hideTouchView();
17379     },
17380     
17381     onTouchViewEmptyResults : function()
17382     {
17383         this.clearTouchView();
17384         
17385         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17386         
17387         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17388         
17389     },
17390     
17391     clearTouchView : function()
17392     {
17393         this.touchViewListGroup.dom.innerHTML = '';
17394     },
17395     
17396     onTouchViewClick : function(e, el, o)
17397     {
17398         e.preventDefault();
17399         
17400         var row = o.row;
17401         var rowIndex = o.rowIndex;
17402         
17403         var r = this.store.getAt(rowIndex);
17404         
17405         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17406             
17407             if(!this.multiple){
17408                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17409                     c.dom.removeAttribute('checked');
17410                 }, this);
17411
17412                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17413
17414                 this.setFromData(r.data);
17415
17416                 var close = this.closeTriggerEl();
17417
17418                 if(close){
17419                     close.show();
17420                 }
17421
17422                 this.hideTouchView();
17423
17424                 this.fireEvent('select', this, r, rowIndex);
17425
17426                 return;
17427             }
17428
17429             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17430                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17431                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17432                 return;
17433             }
17434
17435             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17436             this.addItem(r.data);
17437             this.tickItems.push(r.data);
17438         }
17439     },
17440     
17441     getAutoCreateNativeIOS : function()
17442     {
17443         var cfg = {
17444             cls: 'form-group' //input-group,
17445         };
17446         
17447         var combobox =  {
17448             tag: 'select',
17449             cls : 'roo-ios-select'
17450         };
17451         
17452         if (this.name) {
17453             combobox.name = this.name;
17454         }
17455         
17456         if (this.disabled) {
17457             combobox.disabled = true;
17458         }
17459         
17460         var settings = this;
17461         
17462         ['xs','sm','md','lg'].map(function(size){
17463             if (settings[size]) {
17464                 cfg.cls += ' col-' + size + '-' + settings[size];
17465             }
17466         });
17467         
17468         cfg.cn = combobox;
17469         
17470         return cfg;
17471         
17472     },
17473     
17474     initIOSView : function()
17475     {
17476         this.store.on('load', this.onIOSViewLoad, this);
17477         
17478         return;
17479     },
17480     
17481     onIOSViewLoad : function()
17482     {
17483         if(this.store.getCount() < 1){
17484             return;
17485         }
17486         
17487         this.clearIOSView();
17488         
17489         if(this.allowBlank) {
17490             
17491             var default_text = '-- SELECT --';
17492             
17493             if(this.placeholder.length){
17494                 default_text = this.placeholder;
17495             }
17496             
17497             if(this.emptyTitle.length){
17498                 default_text += ' - ' + this.emptyTitle + ' -';
17499             }
17500             
17501             var opt = this.inputEl().createChild({
17502                 tag: 'option',
17503                 value : 0,
17504                 html : default_text
17505             });
17506             
17507             var o = {};
17508             o[this.valueField] = 0;
17509             o[this.displayField] = default_text;
17510             
17511             this.ios_options.push({
17512                 data : o,
17513                 el : opt
17514             });
17515             
17516         }
17517         
17518         this.store.data.each(function(d, rowIndex){
17519             
17520             var html = '';
17521             
17522             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17523                 html = d.data[this.displayField];
17524             }
17525             
17526             var value = '';
17527             
17528             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17529                 value = d.data[this.valueField];
17530             }
17531             
17532             var option = {
17533                 tag: 'option',
17534                 value : value,
17535                 html : html
17536             };
17537             
17538             if(this.value == d.data[this.valueField]){
17539                 option['selected'] = true;
17540             }
17541             
17542             var opt = this.inputEl().createChild(option);
17543             
17544             this.ios_options.push({
17545                 data : d.data,
17546                 el : opt
17547             });
17548             
17549         }, this);
17550         
17551         this.inputEl().on('change', function(){
17552            this.fireEvent('select', this);
17553         }, this);
17554         
17555     },
17556     
17557     clearIOSView: function()
17558     {
17559         this.inputEl().dom.innerHTML = '';
17560         
17561         this.ios_options = [];
17562     },
17563     
17564     setIOSValue: function(v)
17565     {
17566         this.value = v;
17567         
17568         if(!this.ios_options){
17569             return;
17570         }
17571         
17572         Roo.each(this.ios_options, function(opts){
17573            
17574            opts.el.dom.removeAttribute('selected');
17575            
17576            if(opts.data[this.valueField] != v){
17577                return;
17578            }
17579            
17580            opts.el.dom.setAttribute('selected', true);
17581            
17582         }, this);
17583     }
17584
17585     /** 
17586     * @cfg {Boolean} grow 
17587     * @hide 
17588     */
17589     /** 
17590     * @cfg {Number} growMin 
17591     * @hide 
17592     */
17593     /** 
17594     * @cfg {Number} growMax 
17595     * @hide 
17596     */
17597     /**
17598      * @hide
17599      * @method autoSize
17600      */
17601 });
17602
17603 Roo.apply(Roo.bootstrap.ComboBox,  {
17604     
17605     header : {
17606         tag: 'div',
17607         cls: 'modal-header',
17608         cn: [
17609             {
17610                 tag: 'h4',
17611                 cls: 'modal-title'
17612             }
17613         ]
17614     },
17615     
17616     body : {
17617         tag: 'div',
17618         cls: 'modal-body',
17619         cn: [
17620             {
17621                 tag: 'ul',
17622                 cls: 'list-group'
17623             }
17624         ]
17625     },
17626     
17627     listItemRadio : {
17628         tag: 'li',
17629         cls: 'list-group-item',
17630         cn: [
17631             {
17632                 tag: 'span',
17633                 cls: 'roo-combobox-list-group-item-value'
17634             },
17635             {
17636                 tag: 'div',
17637                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17638                 cn: [
17639                     {
17640                         tag: 'input',
17641                         type: 'radio'
17642                     },
17643                     {
17644                         tag: 'label'
17645                     }
17646                 ]
17647             }
17648         ]
17649     },
17650     
17651     listItemCheckbox : {
17652         tag: 'li',
17653         cls: 'list-group-item',
17654         cn: [
17655             {
17656                 tag: 'span',
17657                 cls: 'roo-combobox-list-group-item-value'
17658             },
17659             {
17660                 tag: 'div',
17661                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17662                 cn: [
17663                     {
17664                         tag: 'input',
17665                         type: 'checkbox'
17666                     },
17667                     {
17668                         tag: 'label'
17669                     }
17670                 ]
17671             }
17672         ]
17673     },
17674     
17675     emptyResult : {
17676         tag: 'div',
17677         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17678     },
17679     
17680     footer : {
17681         tag: 'div',
17682         cls: 'modal-footer',
17683         cn: [
17684             {
17685                 tag: 'div',
17686                 cls: 'row',
17687                 cn: [
17688                     {
17689                         tag: 'div',
17690                         cls: 'col-xs-6 text-left',
17691                         cn: {
17692                             tag: 'button',
17693                             cls: 'btn btn-danger roo-touch-view-cancel',
17694                             html: 'Cancel'
17695                         }
17696                     },
17697                     {
17698                         tag: 'div',
17699                         cls: 'col-xs-6 text-right',
17700                         cn: {
17701                             tag: 'button',
17702                             cls: 'btn btn-success roo-touch-view-ok',
17703                             html: 'OK'
17704                         }
17705                     }
17706                 ]
17707             }
17708         ]
17709         
17710     }
17711 });
17712
17713 Roo.apply(Roo.bootstrap.ComboBox,  {
17714     
17715     touchViewTemplate : {
17716         tag: 'div',
17717         cls: 'modal fade roo-combobox-touch-view',
17718         cn: [
17719             {
17720                 tag: 'div',
17721                 cls: 'modal-dialog',
17722                 style : 'position:fixed', // we have to fix position....
17723                 cn: [
17724                     {
17725                         tag: 'div',
17726                         cls: 'modal-content',
17727                         cn: [
17728                             Roo.bootstrap.ComboBox.header,
17729                             Roo.bootstrap.ComboBox.body,
17730                             Roo.bootstrap.ComboBox.footer
17731                         ]
17732                     }
17733                 ]
17734             }
17735         ]
17736     }
17737 });/*
17738  * Based on:
17739  * Ext JS Library 1.1.1
17740  * Copyright(c) 2006-2007, Ext JS, LLC.
17741  *
17742  * Originally Released Under LGPL - original licence link has changed is not relivant.
17743  *
17744  * Fork - LGPL
17745  * <script type="text/javascript">
17746  */
17747
17748 /**
17749  * @class Roo.View
17750  * @extends Roo.util.Observable
17751  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
17752  * This class also supports single and multi selection modes. <br>
17753  * Create a data model bound view:
17754  <pre><code>
17755  var store = new Roo.data.Store(...);
17756
17757  var view = new Roo.View({
17758     el : "my-element",
17759     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
17760  
17761     singleSelect: true,
17762     selectedClass: "ydataview-selected",
17763     store: store
17764  });
17765
17766  // listen for node click?
17767  view.on("click", function(vw, index, node, e){
17768  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17769  });
17770
17771  // load XML data
17772  dataModel.load("foobar.xml");
17773  </code></pre>
17774  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17775  * <br><br>
17776  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17777  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17778  * 
17779  * Note: old style constructor is still suported (container, template, config)
17780  * 
17781  * @constructor
17782  * Create a new View
17783  * @param {Object} config The config object
17784  * 
17785  */
17786 Roo.View = function(config, depreciated_tpl, depreciated_config){
17787     
17788     this.parent = false;
17789     
17790     if (typeof(depreciated_tpl) == 'undefined') {
17791         // new way.. - universal constructor.
17792         Roo.apply(this, config);
17793         this.el  = Roo.get(this.el);
17794     } else {
17795         // old format..
17796         this.el  = Roo.get(config);
17797         this.tpl = depreciated_tpl;
17798         Roo.apply(this, depreciated_config);
17799     }
17800     this.wrapEl  = this.el.wrap().wrap();
17801     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17802     
17803     
17804     if(typeof(this.tpl) == "string"){
17805         this.tpl = new Roo.Template(this.tpl);
17806     } else {
17807         // support xtype ctors..
17808         this.tpl = new Roo.factory(this.tpl, Roo);
17809     }
17810     
17811     
17812     this.tpl.compile();
17813     
17814     /** @private */
17815     this.addEvents({
17816         /**
17817          * @event beforeclick
17818          * Fires before a click is processed. Returns false to cancel the default action.
17819          * @param {Roo.View} this
17820          * @param {Number} index The index of the target node
17821          * @param {HTMLElement} node The target node
17822          * @param {Roo.EventObject} e The raw event object
17823          */
17824             "beforeclick" : true,
17825         /**
17826          * @event click
17827          * Fires when a template node is clicked.
17828          * @param {Roo.View} this
17829          * @param {Number} index The index of the target node
17830          * @param {HTMLElement} node The target node
17831          * @param {Roo.EventObject} e The raw event object
17832          */
17833             "click" : true,
17834         /**
17835          * @event dblclick
17836          * Fires when a template node is double clicked.
17837          * @param {Roo.View} this
17838          * @param {Number} index The index of the target node
17839          * @param {HTMLElement} node The target node
17840          * @param {Roo.EventObject} e The raw event object
17841          */
17842             "dblclick" : true,
17843         /**
17844          * @event contextmenu
17845          * Fires when a template node is right clicked.
17846          * @param {Roo.View} this
17847          * @param {Number} index The index of the target node
17848          * @param {HTMLElement} node The target node
17849          * @param {Roo.EventObject} e The raw event object
17850          */
17851             "contextmenu" : true,
17852         /**
17853          * @event selectionchange
17854          * Fires when the selected nodes change.
17855          * @param {Roo.View} this
17856          * @param {Array} selections Array of the selected nodes
17857          */
17858             "selectionchange" : true,
17859     
17860         /**
17861          * @event beforeselect
17862          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17863          * @param {Roo.View} this
17864          * @param {HTMLElement} node The node to be selected
17865          * @param {Array} selections Array of currently selected nodes
17866          */
17867             "beforeselect" : true,
17868         /**
17869          * @event preparedata
17870          * Fires on every row to render, to allow you to change the data.
17871          * @param {Roo.View} this
17872          * @param {Object} data to be rendered (change this)
17873          */
17874           "preparedata" : true
17875           
17876           
17877         });
17878
17879
17880
17881     this.el.on({
17882         "click": this.onClick,
17883         "dblclick": this.onDblClick,
17884         "contextmenu": this.onContextMenu,
17885         scope:this
17886     });
17887
17888     this.selections = [];
17889     this.nodes = [];
17890     this.cmp = new Roo.CompositeElementLite([]);
17891     if(this.store){
17892         this.store = Roo.factory(this.store, Roo.data);
17893         this.setStore(this.store, true);
17894     }
17895     
17896     if ( this.footer && this.footer.xtype) {
17897            
17898          var fctr = this.wrapEl.appendChild(document.createElement("div"));
17899         
17900         this.footer.dataSource = this.store;
17901         this.footer.container = fctr;
17902         this.footer = Roo.factory(this.footer, Roo);
17903         fctr.insertFirst(this.el);
17904         
17905         // this is a bit insane - as the paging toolbar seems to detach the el..
17906 //        dom.parentNode.parentNode.parentNode
17907          // they get detached?
17908     }
17909     
17910     
17911     Roo.View.superclass.constructor.call(this);
17912     
17913     
17914 };
17915
17916 Roo.extend(Roo.View, Roo.util.Observable, {
17917     
17918      /**
17919      * @cfg {Roo.data.Store} store Data store to load data from.
17920      */
17921     store : false,
17922     
17923     /**
17924      * @cfg {String|Roo.Element} el The container element.
17925      */
17926     el : '',
17927     
17928     /**
17929      * @cfg {String|Roo.Template} tpl The template used by this View 
17930      */
17931     tpl : false,
17932     /**
17933      * @cfg {String} dataName the named area of the template to use as the data area
17934      *                          Works with domtemplates roo-name="name"
17935      */
17936     dataName: false,
17937     /**
17938      * @cfg {String} selectedClass The css class to add to selected nodes
17939      */
17940     selectedClass : "x-view-selected",
17941      /**
17942      * @cfg {String} emptyText The empty text to show when nothing is loaded.
17943      */
17944     emptyText : "",
17945     
17946     /**
17947      * @cfg {String} text to display on mask (default Loading)
17948      */
17949     mask : false,
17950     /**
17951      * @cfg {Boolean} multiSelect Allow multiple selection
17952      */
17953     multiSelect : false,
17954     /**
17955      * @cfg {Boolean} singleSelect Allow single selection
17956      */
17957     singleSelect:  false,
17958     
17959     /**
17960      * @cfg {Boolean} toggleSelect - selecting 
17961      */
17962     toggleSelect : false,
17963     
17964     /**
17965      * @cfg {Boolean} tickable - selecting 
17966      */
17967     tickable : false,
17968     
17969     /**
17970      * Returns the element this view is bound to.
17971      * @return {Roo.Element}
17972      */
17973     getEl : function(){
17974         return this.wrapEl;
17975     },
17976     
17977     
17978
17979     /**
17980      * Refreshes the view. - called by datachanged on the store. - do not call directly.
17981      */
17982     refresh : function(){
17983         //Roo.log('refresh');
17984         var t = this.tpl;
17985         
17986         // if we are using something like 'domtemplate', then
17987         // the what gets used is:
17988         // t.applySubtemplate(NAME, data, wrapping data..)
17989         // the outer template then get' applied with
17990         //     the store 'extra data'
17991         // and the body get's added to the
17992         //      roo-name="data" node?
17993         //      <span class='roo-tpl-{name}'></span> ?????
17994         
17995         
17996         
17997         this.clearSelections();
17998         this.el.update("");
17999         var html = [];
18000         var records = this.store.getRange();
18001         if(records.length < 1) {
18002             
18003             // is this valid??  = should it render a template??
18004             
18005             this.el.update(this.emptyText);
18006             return;
18007         }
18008         var el = this.el;
18009         if (this.dataName) {
18010             this.el.update(t.apply(this.store.meta)); //????
18011             el = this.el.child('.roo-tpl-' + this.dataName);
18012         }
18013         
18014         for(var i = 0, len = records.length; i < len; i++){
18015             var data = this.prepareData(records[i].data, i, records[i]);
18016             this.fireEvent("preparedata", this, data, i, records[i]);
18017             
18018             var d = Roo.apply({}, data);
18019             
18020             if(this.tickable){
18021                 Roo.apply(d, {'roo-id' : Roo.id()});
18022                 
18023                 var _this = this;
18024             
18025                 Roo.each(this.parent.item, function(item){
18026                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18027                         return;
18028                     }
18029                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18030                 });
18031             }
18032             
18033             html[html.length] = Roo.util.Format.trim(
18034                 this.dataName ?
18035                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18036                     t.apply(d)
18037             );
18038         }
18039         
18040         
18041         
18042         el.update(html.join(""));
18043         this.nodes = el.dom.childNodes;
18044         this.updateIndexes(0);
18045     },
18046     
18047
18048     /**
18049      * Function to override to reformat the data that is sent to
18050      * the template for each node.
18051      * DEPRICATED - use the preparedata event handler.
18052      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18053      * a JSON object for an UpdateManager bound view).
18054      */
18055     prepareData : function(data, index, record)
18056     {
18057         this.fireEvent("preparedata", this, data, index, record);
18058         return data;
18059     },
18060
18061     onUpdate : function(ds, record){
18062         // Roo.log('on update');   
18063         this.clearSelections();
18064         var index = this.store.indexOf(record);
18065         var n = this.nodes[index];
18066         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18067         n.parentNode.removeChild(n);
18068         this.updateIndexes(index, index);
18069     },
18070
18071     
18072     
18073 // --------- FIXME     
18074     onAdd : function(ds, records, index)
18075     {
18076         //Roo.log(['on Add', ds, records, index] );        
18077         this.clearSelections();
18078         if(this.nodes.length == 0){
18079             this.refresh();
18080             return;
18081         }
18082         var n = this.nodes[index];
18083         for(var i = 0, len = records.length; i < len; i++){
18084             var d = this.prepareData(records[i].data, i, records[i]);
18085             if(n){
18086                 this.tpl.insertBefore(n, d);
18087             }else{
18088                 
18089                 this.tpl.append(this.el, d);
18090             }
18091         }
18092         this.updateIndexes(index);
18093     },
18094
18095     onRemove : function(ds, record, index){
18096        // Roo.log('onRemove');
18097         this.clearSelections();
18098         var el = this.dataName  ?
18099             this.el.child('.roo-tpl-' + this.dataName) :
18100             this.el; 
18101         
18102         el.dom.removeChild(this.nodes[index]);
18103         this.updateIndexes(index);
18104     },
18105
18106     /**
18107      * Refresh an individual node.
18108      * @param {Number} index
18109      */
18110     refreshNode : function(index){
18111         this.onUpdate(this.store, this.store.getAt(index));
18112     },
18113
18114     updateIndexes : function(startIndex, endIndex){
18115         var ns = this.nodes;
18116         startIndex = startIndex || 0;
18117         endIndex = endIndex || ns.length - 1;
18118         for(var i = startIndex; i <= endIndex; i++){
18119             ns[i].nodeIndex = i;
18120         }
18121     },
18122
18123     /**
18124      * Changes the data store this view uses and refresh the view.
18125      * @param {Store} store
18126      */
18127     setStore : function(store, initial){
18128         if(!initial && this.store){
18129             this.store.un("datachanged", this.refresh);
18130             this.store.un("add", this.onAdd);
18131             this.store.un("remove", this.onRemove);
18132             this.store.un("update", this.onUpdate);
18133             this.store.un("clear", this.refresh);
18134             this.store.un("beforeload", this.onBeforeLoad);
18135             this.store.un("load", this.onLoad);
18136             this.store.un("loadexception", this.onLoad);
18137         }
18138         if(store){
18139           
18140             store.on("datachanged", this.refresh, this);
18141             store.on("add", this.onAdd, this);
18142             store.on("remove", this.onRemove, this);
18143             store.on("update", this.onUpdate, this);
18144             store.on("clear", this.refresh, this);
18145             store.on("beforeload", this.onBeforeLoad, this);
18146             store.on("load", this.onLoad, this);
18147             store.on("loadexception", this.onLoad, this);
18148         }
18149         
18150         if(store){
18151             this.refresh();
18152         }
18153     },
18154     /**
18155      * onbeforeLoad - masks the loading area.
18156      *
18157      */
18158     onBeforeLoad : function(store,opts)
18159     {
18160          //Roo.log('onBeforeLoad');   
18161         if (!opts.add) {
18162             this.el.update("");
18163         }
18164         this.el.mask(this.mask ? this.mask : "Loading" ); 
18165     },
18166     onLoad : function ()
18167     {
18168         this.el.unmask();
18169     },
18170     
18171
18172     /**
18173      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18174      * @param {HTMLElement} node
18175      * @return {HTMLElement} The template node
18176      */
18177     findItemFromChild : function(node){
18178         var el = this.dataName  ?
18179             this.el.child('.roo-tpl-' + this.dataName,true) :
18180             this.el.dom; 
18181         
18182         if(!node || node.parentNode == el){
18183                     return node;
18184             }
18185             var p = node.parentNode;
18186             while(p && p != el){
18187             if(p.parentNode == el){
18188                 return p;
18189             }
18190             p = p.parentNode;
18191         }
18192             return null;
18193     },
18194
18195     /** @ignore */
18196     onClick : function(e){
18197         var item = this.findItemFromChild(e.getTarget());
18198         if(item){
18199             var index = this.indexOf(item);
18200             if(this.onItemClick(item, index, e) !== false){
18201                 this.fireEvent("click", this, index, item, e);
18202             }
18203         }else{
18204             this.clearSelections();
18205         }
18206     },
18207
18208     /** @ignore */
18209     onContextMenu : function(e){
18210         var item = this.findItemFromChild(e.getTarget());
18211         if(item){
18212             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18213         }
18214     },
18215
18216     /** @ignore */
18217     onDblClick : function(e){
18218         var item = this.findItemFromChild(e.getTarget());
18219         if(item){
18220             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18221         }
18222     },
18223
18224     onItemClick : function(item, index, e)
18225     {
18226         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18227             return false;
18228         }
18229         if (this.toggleSelect) {
18230             var m = this.isSelected(item) ? 'unselect' : 'select';
18231             //Roo.log(m);
18232             var _t = this;
18233             _t[m](item, true, false);
18234             return true;
18235         }
18236         if(this.multiSelect || this.singleSelect){
18237             if(this.multiSelect && e.shiftKey && this.lastSelection){
18238                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18239             }else{
18240                 this.select(item, this.multiSelect && e.ctrlKey);
18241                 this.lastSelection = item;
18242             }
18243             
18244             if(!this.tickable){
18245                 e.preventDefault();
18246             }
18247             
18248         }
18249         return true;
18250     },
18251
18252     /**
18253      * Get the number of selected nodes.
18254      * @return {Number}
18255      */
18256     getSelectionCount : function(){
18257         return this.selections.length;
18258     },
18259
18260     /**
18261      * Get the currently selected nodes.
18262      * @return {Array} An array of HTMLElements
18263      */
18264     getSelectedNodes : function(){
18265         return this.selections;
18266     },
18267
18268     /**
18269      * Get the indexes of the selected nodes.
18270      * @return {Array}
18271      */
18272     getSelectedIndexes : function(){
18273         var indexes = [], s = this.selections;
18274         for(var i = 0, len = s.length; i < len; i++){
18275             indexes.push(s[i].nodeIndex);
18276         }
18277         return indexes;
18278     },
18279
18280     /**
18281      * Clear all selections
18282      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18283      */
18284     clearSelections : function(suppressEvent){
18285         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18286             this.cmp.elements = this.selections;
18287             this.cmp.removeClass(this.selectedClass);
18288             this.selections = [];
18289             if(!suppressEvent){
18290                 this.fireEvent("selectionchange", this, this.selections);
18291             }
18292         }
18293     },
18294
18295     /**
18296      * Returns true if the passed node is selected
18297      * @param {HTMLElement/Number} node The node or node index
18298      * @return {Boolean}
18299      */
18300     isSelected : function(node){
18301         var s = this.selections;
18302         if(s.length < 1){
18303             return false;
18304         }
18305         node = this.getNode(node);
18306         return s.indexOf(node) !== -1;
18307     },
18308
18309     /**
18310      * Selects nodes.
18311      * @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
18312      * @param {Boolean} keepExisting (optional) true to keep existing selections
18313      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18314      */
18315     select : function(nodeInfo, keepExisting, suppressEvent){
18316         if(nodeInfo instanceof Array){
18317             if(!keepExisting){
18318                 this.clearSelections(true);
18319             }
18320             for(var i = 0, len = nodeInfo.length; i < len; i++){
18321                 this.select(nodeInfo[i], true, true);
18322             }
18323             return;
18324         } 
18325         var node = this.getNode(nodeInfo);
18326         if(!node || this.isSelected(node)){
18327             return; // already selected.
18328         }
18329         if(!keepExisting){
18330             this.clearSelections(true);
18331         }
18332         
18333         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18334             Roo.fly(node).addClass(this.selectedClass);
18335             this.selections.push(node);
18336             if(!suppressEvent){
18337                 this.fireEvent("selectionchange", this, this.selections);
18338             }
18339         }
18340         
18341         
18342     },
18343       /**
18344      * Unselects nodes.
18345      * @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
18346      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18347      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18348      */
18349     unselect : function(nodeInfo, keepExisting, suppressEvent)
18350     {
18351         if(nodeInfo instanceof Array){
18352             Roo.each(this.selections, function(s) {
18353                 this.unselect(s, nodeInfo);
18354             }, this);
18355             return;
18356         }
18357         var node = this.getNode(nodeInfo);
18358         if(!node || !this.isSelected(node)){
18359             //Roo.log("not selected");
18360             return; // not selected.
18361         }
18362         // fireevent???
18363         var ns = [];
18364         Roo.each(this.selections, function(s) {
18365             if (s == node ) {
18366                 Roo.fly(node).removeClass(this.selectedClass);
18367
18368                 return;
18369             }
18370             ns.push(s);
18371         },this);
18372         
18373         this.selections= ns;
18374         this.fireEvent("selectionchange", this, this.selections);
18375     },
18376
18377     /**
18378      * Gets a template node.
18379      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18380      * @return {HTMLElement} The node or null if it wasn't found
18381      */
18382     getNode : function(nodeInfo){
18383         if(typeof nodeInfo == "string"){
18384             return document.getElementById(nodeInfo);
18385         }else if(typeof nodeInfo == "number"){
18386             return this.nodes[nodeInfo];
18387         }
18388         return nodeInfo;
18389     },
18390
18391     /**
18392      * Gets a range template nodes.
18393      * @param {Number} startIndex
18394      * @param {Number} endIndex
18395      * @return {Array} An array of nodes
18396      */
18397     getNodes : function(start, end){
18398         var ns = this.nodes;
18399         start = start || 0;
18400         end = typeof end == "undefined" ? ns.length - 1 : end;
18401         var nodes = [];
18402         if(start <= end){
18403             for(var i = start; i <= end; i++){
18404                 nodes.push(ns[i]);
18405             }
18406         } else{
18407             for(var i = start; i >= end; i--){
18408                 nodes.push(ns[i]);
18409             }
18410         }
18411         return nodes;
18412     },
18413
18414     /**
18415      * Finds the index of the passed node
18416      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18417      * @return {Number} The index of the node or -1
18418      */
18419     indexOf : function(node){
18420         node = this.getNode(node);
18421         if(typeof node.nodeIndex == "number"){
18422             return node.nodeIndex;
18423         }
18424         var ns = this.nodes;
18425         for(var i = 0, len = ns.length; i < len; i++){
18426             if(ns[i] == node){
18427                 return i;
18428             }
18429         }
18430         return -1;
18431     }
18432 });
18433 /*
18434  * - LGPL
18435  *
18436  * based on jquery fullcalendar
18437  * 
18438  */
18439
18440 Roo.bootstrap = Roo.bootstrap || {};
18441 /**
18442  * @class Roo.bootstrap.Calendar
18443  * @extends Roo.bootstrap.Component
18444  * Bootstrap Calendar class
18445  * @cfg {Boolean} loadMask (true|false) default false
18446  * @cfg {Object} header generate the user specific header of the calendar, default false
18447
18448  * @constructor
18449  * Create a new Container
18450  * @param {Object} config The config object
18451  */
18452
18453
18454
18455 Roo.bootstrap.Calendar = function(config){
18456     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18457      this.addEvents({
18458         /**
18459              * @event select
18460              * Fires when a date is selected
18461              * @param {DatePicker} this
18462              * @param {Date} date The selected date
18463              */
18464         'select': true,
18465         /**
18466              * @event monthchange
18467              * Fires when the displayed month changes 
18468              * @param {DatePicker} this
18469              * @param {Date} date The selected month
18470              */
18471         'monthchange': true,
18472         /**
18473              * @event evententer
18474              * Fires when mouse over an event
18475              * @param {Calendar} this
18476              * @param {event} Event
18477              */
18478         'evententer': true,
18479         /**
18480              * @event eventleave
18481              * Fires when the mouse leaves an
18482              * @param {Calendar} this
18483              * @param {event}
18484              */
18485         'eventleave': true,
18486         /**
18487              * @event eventclick
18488              * Fires when the mouse click an
18489              * @param {Calendar} this
18490              * @param {event}
18491              */
18492         'eventclick': true
18493         
18494     });
18495
18496 };
18497
18498 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18499     
18500      /**
18501      * @cfg {Number} startDay
18502      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18503      */
18504     startDay : 0,
18505     
18506     loadMask : false,
18507     
18508     header : false,
18509       
18510     getAutoCreate : function(){
18511         
18512         
18513         var fc_button = function(name, corner, style, content ) {
18514             return Roo.apply({},{
18515                 tag : 'span',
18516                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18517                          (corner.length ?
18518                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18519                             ''
18520                         ),
18521                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18522                 unselectable: 'on'
18523             });
18524         };
18525         
18526         var header = {};
18527         
18528         if(!this.header){
18529             header = {
18530                 tag : 'table',
18531                 cls : 'fc-header',
18532                 style : 'width:100%',
18533                 cn : [
18534                     {
18535                         tag: 'tr',
18536                         cn : [
18537                             {
18538                                 tag : 'td',
18539                                 cls : 'fc-header-left',
18540                                 cn : [
18541                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18542                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18543                                     { tag: 'span', cls: 'fc-header-space' },
18544                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18545
18546
18547                                 ]
18548                             },
18549
18550                             {
18551                                 tag : 'td',
18552                                 cls : 'fc-header-center',
18553                                 cn : [
18554                                     {
18555                                         tag: 'span',
18556                                         cls: 'fc-header-title',
18557                                         cn : {
18558                                             tag: 'H2',
18559                                             html : 'month / year'
18560                                         }
18561                                     }
18562
18563                                 ]
18564                             },
18565                             {
18566                                 tag : 'td',
18567                                 cls : 'fc-header-right',
18568                                 cn : [
18569                               /*      fc_button('month', 'left', '', 'month' ),
18570                                     fc_button('week', '', '', 'week' ),
18571                                     fc_button('day', 'right', '', 'day' )
18572                                 */    
18573
18574                                 ]
18575                             }
18576
18577                         ]
18578                     }
18579                 ]
18580             };
18581         }
18582         
18583         header = this.header;
18584         
18585        
18586         var cal_heads = function() {
18587             var ret = [];
18588             // fixme - handle this.
18589             
18590             for (var i =0; i < Date.dayNames.length; i++) {
18591                 var d = Date.dayNames[i];
18592                 ret.push({
18593                     tag: 'th',
18594                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18595                     html : d.substring(0,3)
18596                 });
18597                 
18598             }
18599             ret[0].cls += ' fc-first';
18600             ret[6].cls += ' fc-last';
18601             return ret;
18602         };
18603         var cal_cell = function(n) {
18604             return  {
18605                 tag: 'td',
18606                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18607                 cn : [
18608                     {
18609                         cn : [
18610                             {
18611                                 cls: 'fc-day-number',
18612                                 html: 'D'
18613                             },
18614                             {
18615                                 cls: 'fc-day-content',
18616                              
18617                                 cn : [
18618                                      {
18619                                         style: 'position: relative;' // height: 17px;
18620                                     }
18621                                 ]
18622                             }
18623                             
18624                             
18625                         ]
18626                     }
18627                 ]
18628                 
18629             }
18630         };
18631         var cal_rows = function() {
18632             
18633             var ret = [];
18634             for (var r = 0; r < 6; r++) {
18635                 var row= {
18636                     tag : 'tr',
18637                     cls : 'fc-week',
18638                     cn : []
18639                 };
18640                 
18641                 for (var i =0; i < Date.dayNames.length; i++) {
18642                     var d = Date.dayNames[i];
18643                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18644
18645                 }
18646                 row.cn[0].cls+=' fc-first';
18647                 row.cn[0].cn[0].style = 'min-height:90px';
18648                 row.cn[6].cls+=' fc-last';
18649                 ret.push(row);
18650                 
18651             }
18652             ret[0].cls += ' fc-first';
18653             ret[4].cls += ' fc-prev-last';
18654             ret[5].cls += ' fc-last';
18655             return ret;
18656             
18657         };
18658         
18659         var cal_table = {
18660             tag: 'table',
18661             cls: 'fc-border-separate',
18662             style : 'width:100%',
18663             cellspacing  : 0,
18664             cn : [
18665                 { 
18666                     tag: 'thead',
18667                     cn : [
18668                         { 
18669                             tag: 'tr',
18670                             cls : 'fc-first fc-last',
18671                             cn : cal_heads()
18672                         }
18673                     ]
18674                 },
18675                 { 
18676                     tag: 'tbody',
18677                     cn : cal_rows()
18678                 }
18679                   
18680             ]
18681         };
18682          
18683          var cfg = {
18684             cls : 'fc fc-ltr',
18685             cn : [
18686                 header,
18687                 {
18688                     cls : 'fc-content',
18689                     style : "position: relative;",
18690                     cn : [
18691                         {
18692                             cls : 'fc-view fc-view-month fc-grid',
18693                             style : 'position: relative',
18694                             unselectable : 'on',
18695                             cn : [
18696                                 {
18697                                     cls : 'fc-event-container',
18698                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18699                                 },
18700                                 cal_table
18701                             ]
18702                         }
18703                     ]
18704     
18705                 }
18706            ] 
18707             
18708         };
18709         
18710          
18711         
18712         return cfg;
18713     },
18714     
18715     
18716     initEvents : function()
18717     {
18718         if(!this.store){
18719             throw "can not find store for calendar";
18720         }
18721         
18722         var mark = {
18723             tag: "div",
18724             cls:"x-dlg-mask",
18725             style: "text-align:center",
18726             cn: [
18727                 {
18728                     tag: "div",
18729                     style: "background-color:white;width:50%;margin:250 auto",
18730                     cn: [
18731                         {
18732                             tag: "img",
18733                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18734                         },
18735                         {
18736                             tag: "span",
18737                             html: "Loading"
18738                         }
18739                         
18740                     ]
18741                 }
18742             ]
18743         };
18744         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18745         
18746         var size = this.el.select('.fc-content', true).first().getSize();
18747         this.maskEl.setSize(size.width, size.height);
18748         this.maskEl.enableDisplayMode("block");
18749         if(!this.loadMask){
18750             this.maskEl.hide();
18751         }
18752         
18753         this.store = Roo.factory(this.store, Roo.data);
18754         this.store.on('load', this.onLoad, this);
18755         this.store.on('beforeload', this.onBeforeLoad, this);
18756         
18757         this.resize();
18758         
18759         this.cells = this.el.select('.fc-day',true);
18760         //Roo.log(this.cells);
18761         this.textNodes = this.el.query('.fc-day-number');
18762         this.cells.addClassOnOver('fc-state-hover');
18763         
18764         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18765         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18766         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18767         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18768         
18769         this.on('monthchange', this.onMonthChange, this);
18770         
18771         this.update(new Date().clearTime());
18772     },
18773     
18774     resize : function() {
18775         var sz  = this.el.getSize();
18776         
18777         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18778         this.el.select('.fc-day-content div',true).setHeight(34);
18779     },
18780     
18781     
18782     // private
18783     showPrevMonth : function(e){
18784         this.update(this.activeDate.add("mo", -1));
18785     },
18786     showToday : function(e){
18787         this.update(new Date().clearTime());
18788     },
18789     // private
18790     showNextMonth : function(e){
18791         this.update(this.activeDate.add("mo", 1));
18792     },
18793
18794     // private
18795     showPrevYear : function(){
18796         this.update(this.activeDate.add("y", -1));
18797     },
18798
18799     // private
18800     showNextYear : function(){
18801         this.update(this.activeDate.add("y", 1));
18802     },
18803
18804     
18805    // private
18806     update : function(date)
18807     {
18808         var vd = this.activeDate;
18809         this.activeDate = date;
18810 //        if(vd && this.el){
18811 //            var t = date.getTime();
18812 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18813 //                Roo.log('using add remove');
18814 //                
18815 //                this.fireEvent('monthchange', this, date);
18816 //                
18817 //                this.cells.removeClass("fc-state-highlight");
18818 //                this.cells.each(function(c){
18819 //                   if(c.dateValue == t){
18820 //                       c.addClass("fc-state-highlight");
18821 //                       setTimeout(function(){
18822 //                            try{c.dom.firstChild.focus();}catch(e){}
18823 //                       }, 50);
18824 //                       return false;
18825 //                   }
18826 //                   return true;
18827 //                });
18828 //                return;
18829 //            }
18830 //        }
18831         
18832         var days = date.getDaysInMonth();
18833         
18834         var firstOfMonth = date.getFirstDateOfMonth();
18835         var startingPos = firstOfMonth.getDay()-this.startDay;
18836         
18837         if(startingPos < this.startDay){
18838             startingPos += 7;
18839         }
18840         
18841         var pm = date.add(Date.MONTH, -1);
18842         var prevStart = pm.getDaysInMonth()-startingPos;
18843 //        
18844         this.cells = this.el.select('.fc-day',true);
18845         this.textNodes = this.el.query('.fc-day-number');
18846         this.cells.addClassOnOver('fc-state-hover');
18847         
18848         var cells = this.cells.elements;
18849         var textEls = this.textNodes;
18850         
18851         Roo.each(cells, function(cell){
18852             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18853         });
18854         
18855         days += startingPos;
18856
18857         // convert everything to numbers so it's fast
18858         var day = 86400000;
18859         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18860         //Roo.log(d);
18861         //Roo.log(pm);
18862         //Roo.log(prevStart);
18863         
18864         var today = new Date().clearTime().getTime();
18865         var sel = date.clearTime().getTime();
18866         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18867         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18868         var ddMatch = this.disabledDatesRE;
18869         var ddText = this.disabledDatesText;
18870         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18871         var ddaysText = this.disabledDaysText;
18872         var format = this.format;
18873         
18874         var setCellClass = function(cal, cell){
18875             cell.row = 0;
18876             cell.events = [];
18877             cell.more = [];
18878             //Roo.log('set Cell Class');
18879             cell.title = "";
18880             var t = d.getTime();
18881             
18882             //Roo.log(d);
18883             
18884             cell.dateValue = t;
18885             if(t == today){
18886                 cell.className += " fc-today";
18887                 cell.className += " fc-state-highlight";
18888                 cell.title = cal.todayText;
18889             }
18890             if(t == sel){
18891                 // disable highlight in other month..
18892                 //cell.className += " fc-state-highlight";
18893                 
18894             }
18895             // disabling
18896             if(t < min) {
18897                 cell.className = " fc-state-disabled";
18898                 cell.title = cal.minText;
18899                 return;
18900             }
18901             if(t > max) {
18902                 cell.className = " fc-state-disabled";
18903                 cell.title = cal.maxText;
18904                 return;
18905             }
18906             if(ddays){
18907                 if(ddays.indexOf(d.getDay()) != -1){
18908                     cell.title = ddaysText;
18909                     cell.className = " fc-state-disabled";
18910                 }
18911             }
18912             if(ddMatch && format){
18913                 var fvalue = d.dateFormat(format);
18914                 if(ddMatch.test(fvalue)){
18915                     cell.title = ddText.replace("%0", fvalue);
18916                     cell.className = " fc-state-disabled";
18917                 }
18918             }
18919             
18920             if (!cell.initialClassName) {
18921                 cell.initialClassName = cell.dom.className;
18922             }
18923             
18924             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
18925         };
18926
18927         var i = 0;
18928         
18929         for(; i < startingPos; i++) {
18930             textEls[i].innerHTML = (++prevStart);
18931             d.setDate(d.getDate()+1);
18932             
18933             cells[i].className = "fc-past fc-other-month";
18934             setCellClass(this, cells[i]);
18935         }
18936         
18937         var intDay = 0;
18938         
18939         for(; i < days; i++){
18940             intDay = i - startingPos + 1;
18941             textEls[i].innerHTML = (intDay);
18942             d.setDate(d.getDate()+1);
18943             
18944             cells[i].className = ''; // "x-date-active";
18945             setCellClass(this, cells[i]);
18946         }
18947         var extraDays = 0;
18948         
18949         for(; i < 42; i++) {
18950             textEls[i].innerHTML = (++extraDays);
18951             d.setDate(d.getDate()+1);
18952             
18953             cells[i].className = "fc-future fc-other-month";
18954             setCellClass(this, cells[i]);
18955         }
18956         
18957         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
18958         
18959         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
18960         
18961         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
18962         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
18963         
18964         if(totalRows != 6){
18965             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
18966             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
18967         }
18968         
18969         this.fireEvent('monthchange', this, date);
18970         
18971         
18972         /*
18973         if(!this.internalRender){
18974             var main = this.el.dom.firstChild;
18975             var w = main.offsetWidth;
18976             this.el.setWidth(w + this.el.getBorderWidth("lr"));
18977             Roo.fly(main).setWidth(w);
18978             this.internalRender = true;
18979             // opera does not respect the auto grow header center column
18980             // then, after it gets a width opera refuses to recalculate
18981             // without a second pass
18982             if(Roo.isOpera && !this.secondPass){
18983                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
18984                 this.secondPass = true;
18985                 this.update.defer(10, this, [date]);
18986             }
18987         }
18988         */
18989         
18990     },
18991     
18992     findCell : function(dt) {
18993         dt = dt.clearTime().getTime();
18994         var ret = false;
18995         this.cells.each(function(c){
18996             //Roo.log("check " +c.dateValue + '?=' + dt);
18997             if(c.dateValue == dt){
18998                 ret = c;
18999                 return false;
19000             }
19001             return true;
19002         });
19003         
19004         return ret;
19005     },
19006     
19007     findCells : function(ev) {
19008         var s = ev.start.clone().clearTime().getTime();
19009        // Roo.log(s);
19010         var e= ev.end.clone().clearTime().getTime();
19011        // Roo.log(e);
19012         var ret = [];
19013         this.cells.each(function(c){
19014              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19015             
19016             if(c.dateValue > e){
19017                 return ;
19018             }
19019             if(c.dateValue < s){
19020                 return ;
19021             }
19022             ret.push(c);
19023         });
19024         
19025         return ret;    
19026     },
19027     
19028 //    findBestRow: function(cells)
19029 //    {
19030 //        var ret = 0;
19031 //        
19032 //        for (var i =0 ; i < cells.length;i++) {
19033 //            ret  = Math.max(cells[i].rows || 0,ret);
19034 //        }
19035 //        return ret;
19036 //        
19037 //    },
19038     
19039     
19040     addItem : function(ev)
19041     {
19042         // look for vertical location slot in
19043         var cells = this.findCells(ev);
19044         
19045 //        ev.row = this.findBestRow(cells);
19046         
19047         // work out the location.
19048         
19049         var crow = false;
19050         var rows = [];
19051         for(var i =0; i < cells.length; i++) {
19052             
19053             cells[i].row = cells[0].row;
19054             
19055             if(i == 0){
19056                 cells[i].row = cells[i].row + 1;
19057             }
19058             
19059             if (!crow) {
19060                 crow = {
19061                     start : cells[i],
19062                     end :  cells[i]
19063                 };
19064                 continue;
19065             }
19066             if (crow.start.getY() == cells[i].getY()) {
19067                 // on same row.
19068                 crow.end = cells[i];
19069                 continue;
19070             }
19071             // different row.
19072             rows.push(crow);
19073             crow = {
19074                 start: cells[i],
19075                 end : cells[i]
19076             };
19077             
19078         }
19079         
19080         rows.push(crow);
19081         ev.els = [];
19082         ev.rows = rows;
19083         ev.cells = cells;
19084         
19085         cells[0].events.push(ev);
19086         
19087         this.calevents.push(ev);
19088     },
19089     
19090     clearEvents: function() {
19091         
19092         if(!this.calevents){
19093             return;
19094         }
19095         
19096         Roo.each(this.cells.elements, function(c){
19097             c.row = 0;
19098             c.events = [];
19099             c.more = [];
19100         });
19101         
19102         Roo.each(this.calevents, function(e) {
19103             Roo.each(e.els, function(el) {
19104                 el.un('mouseenter' ,this.onEventEnter, this);
19105                 el.un('mouseleave' ,this.onEventLeave, this);
19106                 el.remove();
19107             },this);
19108         },this);
19109         
19110         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19111             e.remove();
19112         });
19113         
19114     },
19115     
19116     renderEvents: function()
19117     {   
19118         var _this = this;
19119         
19120         this.cells.each(function(c) {
19121             
19122             if(c.row < 5){
19123                 return;
19124             }
19125             
19126             var ev = c.events;
19127             
19128             var r = 4;
19129             if(c.row != c.events.length){
19130                 r = 4 - (4 - (c.row - c.events.length));
19131             }
19132             
19133             c.events = ev.slice(0, r);
19134             c.more = ev.slice(r);
19135             
19136             if(c.more.length && c.more.length == 1){
19137                 c.events.push(c.more.pop());
19138             }
19139             
19140             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19141             
19142         });
19143             
19144         this.cells.each(function(c) {
19145             
19146             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19147             
19148             
19149             for (var e = 0; e < c.events.length; e++){
19150                 var ev = c.events[e];
19151                 var rows = ev.rows;
19152                 
19153                 for(var i = 0; i < rows.length; i++) {
19154                 
19155                     // how many rows should it span..
19156
19157                     var  cfg = {
19158                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19159                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19160
19161                         unselectable : "on",
19162                         cn : [
19163                             {
19164                                 cls: 'fc-event-inner',
19165                                 cn : [
19166     //                                {
19167     //                                  tag:'span',
19168     //                                  cls: 'fc-event-time',
19169     //                                  html : cells.length > 1 ? '' : ev.time
19170     //                                },
19171                                     {
19172                                       tag:'span',
19173                                       cls: 'fc-event-title',
19174                                       html : String.format('{0}', ev.title)
19175                                     }
19176
19177
19178                                 ]
19179                             },
19180                             {
19181                                 cls: 'ui-resizable-handle ui-resizable-e',
19182                                 html : '&nbsp;&nbsp;&nbsp'
19183                             }
19184
19185                         ]
19186                     };
19187
19188                     if (i == 0) {
19189                         cfg.cls += ' fc-event-start';
19190                     }
19191                     if ((i+1) == rows.length) {
19192                         cfg.cls += ' fc-event-end';
19193                     }
19194
19195                     var ctr = _this.el.select('.fc-event-container',true).first();
19196                     var cg = ctr.createChild(cfg);
19197
19198                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19199                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19200
19201                     var r = (c.more.length) ? 1 : 0;
19202                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19203                     cg.setWidth(ebox.right - sbox.x -2);
19204
19205                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19206                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19207                     cg.on('click', _this.onEventClick, _this, ev);
19208
19209                     ev.els.push(cg);
19210                     
19211                 }
19212                 
19213             }
19214             
19215             
19216             if(c.more.length){
19217                 var  cfg = {
19218                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19219                     style : 'position: absolute',
19220                     unselectable : "on",
19221                     cn : [
19222                         {
19223                             cls: 'fc-event-inner',
19224                             cn : [
19225                                 {
19226                                   tag:'span',
19227                                   cls: 'fc-event-title',
19228                                   html : 'More'
19229                                 }
19230
19231
19232                             ]
19233                         },
19234                         {
19235                             cls: 'ui-resizable-handle ui-resizable-e',
19236                             html : '&nbsp;&nbsp;&nbsp'
19237                         }
19238
19239                     ]
19240                 };
19241
19242                 var ctr = _this.el.select('.fc-event-container',true).first();
19243                 var cg = ctr.createChild(cfg);
19244
19245                 var sbox = c.select('.fc-day-content',true).first().getBox();
19246                 var ebox = c.select('.fc-day-content',true).first().getBox();
19247                 //Roo.log(cg);
19248                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19249                 cg.setWidth(ebox.right - sbox.x -2);
19250
19251                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19252                 
19253             }
19254             
19255         });
19256         
19257         
19258         
19259     },
19260     
19261     onEventEnter: function (e, el,event,d) {
19262         this.fireEvent('evententer', this, el, event);
19263     },
19264     
19265     onEventLeave: function (e, el,event,d) {
19266         this.fireEvent('eventleave', this, el, event);
19267     },
19268     
19269     onEventClick: function (e, el,event,d) {
19270         this.fireEvent('eventclick', this, el, event);
19271     },
19272     
19273     onMonthChange: function () {
19274         this.store.load();
19275     },
19276     
19277     onMoreEventClick: function(e, el, more)
19278     {
19279         var _this = this;
19280         
19281         this.calpopover.placement = 'right';
19282         this.calpopover.setTitle('More');
19283         
19284         this.calpopover.setContent('');
19285         
19286         var ctr = this.calpopover.el.select('.popover-content', true).first();
19287         
19288         Roo.each(more, function(m){
19289             var cfg = {
19290                 cls : 'fc-event-hori fc-event-draggable',
19291                 html : m.title
19292             };
19293             var cg = ctr.createChild(cfg);
19294             
19295             cg.on('click', _this.onEventClick, _this, m);
19296         });
19297         
19298         this.calpopover.show(el);
19299         
19300         
19301     },
19302     
19303     onLoad: function () 
19304     {   
19305         this.calevents = [];
19306         var cal = this;
19307         
19308         if(this.store.getCount() > 0){
19309             this.store.data.each(function(d){
19310                cal.addItem({
19311                     id : d.data.id,
19312                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19313                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19314                     time : d.data.start_time,
19315                     title : d.data.title,
19316                     description : d.data.description,
19317                     venue : d.data.venue
19318                 });
19319             });
19320         }
19321         
19322         this.renderEvents();
19323         
19324         if(this.calevents.length && this.loadMask){
19325             this.maskEl.hide();
19326         }
19327     },
19328     
19329     onBeforeLoad: function()
19330     {
19331         this.clearEvents();
19332         if(this.loadMask){
19333             this.maskEl.show();
19334         }
19335     }
19336 });
19337
19338  
19339  /*
19340  * - LGPL
19341  *
19342  * element
19343  * 
19344  */
19345
19346 /**
19347  * @class Roo.bootstrap.Popover
19348  * @extends Roo.bootstrap.Component
19349  * Bootstrap Popover class
19350  * @cfg {String} html contents of the popover   (or false to use children..)
19351  * @cfg {String} title of popover (or false to hide)
19352  * @cfg {String} placement how it is placed
19353  * @cfg {String} trigger click || hover (or false to trigger manually)
19354  * @cfg {String} over what (parent or false to trigger manually.)
19355  * @cfg {Number} delay - delay before showing
19356  
19357  * @constructor
19358  * Create a new Popover
19359  * @param {Object} config The config object
19360  */
19361
19362 Roo.bootstrap.Popover = function(config){
19363     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19364     
19365     this.addEvents({
19366         // raw events
19367          /**
19368          * @event show
19369          * After the popover show
19370          * 
19371          * @param {Roo.bootstrap.Popover} this
19372          */
19373         "show" : true,
19374         /**
19375          * @event hide
19376          * After the popover hide
19377          * 
19378          * @param {Roo.bootstrap.Popover} this
19379          */
19380         "hide" : true
19381     });
19382 };
19383
19384 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19385     
19386     title: 'Fill in a title',
19387     html: false,
19388     
19389     placement : 'right',
19390     trigger : 'hover', // hover
19391     
19392     delay : 0,
19393     
19394     over: 'parent',
19395     
19396     can_build_overlaid : false,
19397     
19398     getChildContainer : function()
19399     {
19400         return this.el.select('.popover-content',true).first();
19401     },
19402     
19403     getAutoCreate : function(){
19404          
19405         var cfg = {
19406            cls : 'popover roo-dynamic',
19407            style: 'display:block',
19408            cn : [
19409                 {
19410                     cls : 'arrow'
19411                 },
19412                 {
19413                     cls : 'popover-inner',
19414                     cn : [
19415                         {
19416                             tag: 'h3',
19417                             cls: 'popover-title popover-header',
19418                             html : this.title
19419                         },
19420                         {
19421                             cls : 'popover-content popover-body',
19422                             html : this.html
19423                         }
19424                     ]
19425                     
19426                 }
19427            ]
19428         };
19429         
19430         return cfg;
19431     },
19432     setTitle: function(str)
19433     {
19434         this.title = str;
19435         this.el.select('.popover-title',true).first().dom.innerHTML = str;
19436     },
19437     setContent: function(str)
19438     {
19439         this.html = str;
19440         this.el.select('.popover-content',true).first().dom.innerHTML = str;
19441     },
19442     // as it get's added to the bottom of the page.
19443     onRender : function(ct, position)
19444     {
19445         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19446         if(!this.el){
19447             var cfg = Roo.apply({},  this.getAutoCreate());
19448             cfg.id = Roo.id();
19449             
19450             if (this.cls) {
19451                 cfg.cls += ' ' + this.cls;
19452             }
19453             if (this.style) {
19454                 cfg.style = this.style;
19455             }
19456             //Roo.log("adding to ");
19457             this.el = Roo.get(document.body).createChild(cfg, position);
19458 //            Roo.log(this.el);
19459         }
19460         this.initEvents();
19461     },
19462     
19463     initEvents : function()
19464     {
19465         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19466         this.el.enableDisplayMode('block');
19467         this.el.hide();
19468         if (this.over === false) {
19469             return; 
19470         }
19471         if (this.triggers === false) {
19472             return;
19473         }
19474         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19475         var triggers = this.trigger ? this.trigger.split(' ') : [];
19476         Roo.each(triggers, function(trigger) {
19477         
19478             if (trigger == 'click') {
19479                 on_el.on('click', this.toggle, this);
19480             } else if (trigger != 'manual') {
19481                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19482                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19483       
19484                 on_el.on(eventIn  ,this.enter, this);
19485                 on_el.on(eventOut, this.leave, this);
19486             }
19487         }, this);
19488         
19489     },
19490     
19491     
19492     // private
19493     timeout : null,
19494     hoverState : null,
19495     
19496     toggle : function () {
19497         this.hoverState == 'in' ? this.leave() : this.enter();
19498     },
19499     
19500     enter : function () {
19501         
19502         clearTimeout(this.timeout);
19503     
19504         this.hoverState = 'in';
19505     
19506         if (!this.delay || !this.delay.show) {
19507             this.show();
19508             return;
19509         }
19510         var _t = this;
19511         this.timeout = setTimeout(function () {
19512             if (_t.hoverState == 'in') {
19513                 _t.show();
19514             }
19515         }, this.delay.show)
19516     },
19517     
19518     leave : function() {
19519         clearTimeout(this.timeout);
19520     
19521         this.hoverState = 'out';
19522     
19523         if (!this.delay || !this.delay.hide) {
19524             this.hide();
19525             return;
19526         }
19527         var _t = this;
19528         this.timeout = setTimeout(function () {
19529             if (_t.hoverState == 'out') {
19530                 _t.hide();
19531             }
19532         }, this.delay.hide)
19533     },
19534     
19535     show : function (on_el)
19536     {
19537         if (!on_el) {
19538             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19539         }
19540         
19541         // set content.
19542         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19543         if (this.html !== false) {
19544             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19545         }
19546         this.el.removeClass([
19547             'fade','top','bottom', 'left', 'right','in',
19548             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19549         ]);
19550         if (!this.title.length) {
19551             this.el.select('.popover-title',true).hide();
19552         }
19553         
19554         var placement = typeof this.placement == 'function' ?
19555             this.placement.call(this, this.el, on_el) :
19556             this.placement;
19557             
19558         var autoToken = /\s?auto?\s?/i;
19559         var autoPlace = autoToken.test(placement);
19560         if (autoPlace) {
19561             placement = placement.replace(autoToken, '') || 'top';
19562         }
19563         
19564         //this.el.detach()
19565         //this.el.setXY([0,0]);
19566         this.el.show();
19567         this.el.dom.style.display='block';
19568         this.el.addClass(placement);
19569         
19570         //this.el.appendTo(on_el);
19571         
19572         var p = this.getPosition();
19573         var box = this.el.getBox();
19574         
19575         if (autoPlace) {
19576             // fixme..
19577         }
19578         var align = Roo.bootstrap.Popover.alignment[placement];
19579         
19580 //        Roo.log(align);
19581         this.el.alignTo(on_el, align[0],align[1]);
19582         //var arrow = this.el.select('.arrow',true).first();
19583         //arrow.set(align[2], 
19584         
19585         this.el.addClass('in');
19586         
19587         
19588         if (this.el.hasClass('fade')) {
19589             // fade it?
19590         }
19591         
19592         this.hoverState = 'in';
19593         
19594         this.fireEvent('show', this);
19595         
19596     },
19597     hide : function()
19598     {
19599         this.el.setXY([0,0]);
19600         this.el.removeClass('in');
19601         this.el.hide();
19602         this.hoverState = null;
19603         
19604         this.fireEvent('hide', this);
19605     }
19606     
19607 });
19608
19609 Roo.bootstrap.Popover.alignment = {
19610     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19611     'right' : ['l-r', [10,0], 'left bs-popover-left'],
19612     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19613     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19614 };
19615
19616  /*
19617  * - LGPL
19618  *
19619  * Progress
19620  * 
19621  */
19622
19623 /**
19624  * @class Roo.bootstrap.Progress
19625  * @extends Roo.bootstrap.Component
19626  * Bootstrap Progress class
19627  * @cfg {Boolean} striped striped of the progress bar
19628  * @cfg {Boolean} active animated of the progress bar
19629  * 
19630  * 
19631  * @constructor
19632  * Create a new Progress
19633  * @param {Object} config The config object
19634  */
19635
19636 Roo.bootstrap.Progress = function(config){
19637     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19638 };
19639
19640 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
19641     
19642     striped : false,
19643     active: false,
19644     
19645     getAutoCreate : function(){
19646         var cfg = {
19647             tag: 'div',
19648             cls: 'progress'
19649         };
19650         
19651         
19652         if(this.striped){
19653             cfg.cls += ' progress-striped';
19654         }
19655       
19656         if(this.active){
19657             cfg.cls += ' active';
19658         }
19659         
19660         
19661         return cfg;
19662     }
19663    
19664 });
19665
19666  
19667
19668  /*
19669  * - LGPL
19670  *
19671  * ProgressBar
19672  * 
19673  */
19674
19675 /**
19676  * @class Roo.bootstrap.ProgressBar
19677  * @extends Roo.bootstrap.Component
19678  * Bootstrap ProgressBar class
19679  * @cfg {Number} aria_valuenow aria-value now
19680  * @cfg {Number} aria_valuemin aria-value min
19681  * @cfg {Number} aria_valuemax aria-value max
19682  * @cfg {String} label label for the progress bar
19683  * @cfg {String} panel (success | info | warning | danger )
19684  * @cfg {String} role role of the progress bar
19685  * @cfg {String} sr_only text
19686  * 
19687  * 
19688  * @constructor
19689  * Create a new ProgressBar
19690  * @param {Object} config The config object
19691  */
19692
19693 Roo.bootstrap.ProgressBar = function(config){
19694     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19695 };
19696
19697 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
19698     
19699     aria_valuenow : 0,
19700     aria_valuemin : 0,
19701     aria_valuemax : 100,
19702     label : false,
19703     panel : false,
19704     role : false,
19705     sr_only: false,
19706     
19707     getAutoCreate : function()
19708     {
19709         
19710         var cfg = {
19711             tag: 'div',
19712             cls: 'progress-bar',
19713             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19714         };
19715         
19716         if(this.sr_only){
19717             cfg.cn = {
19718                 tag: 'span',
19719                 cls: 'sr-only',
19720                 html: this.sr_only
19721             }
19722         }
19723         
19724         if(this.role){
19725             cfg.role = this.role;
19726         }
19727         
19728         if(this.aria_valuenow){
19729             cfg['aria-valuenow'] = this.aria_valuenow;
19730         }
19731         
19732         if(this.aria_valuemin){
19733             cfg['aria-valuemin'] = this.aria_valuemin;
19734         }
19735         
19736         if(this.aria_valuemax){
19737             cfg['aria-valuemax'] = this.aria_valuemax;
19738         }
19739         
19740         if(this.label && !this.sr_only){
19741             cfg.html = this.label;
19742         }
19743         
19744         if(this.panel){
19745             cfg.cls += ' progress-bar-' + this.panel;
19746         }
19747         
19748         return cfg;
19749     },
19750     
19751     update : function(aria_valuenow)
19752     {
19753         this.aria_valuenow = aria_valuenow;
19754         
19755         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19756     }
19757    
19758 });
19759
19760  
19761
19762  /*
19763  * - LGPL
19764  *
19765  * column
19766  * 
19767  */
19768
19769 /**
19770  * @class Roo.bootstrap.TabGroup
19771  * @extends Roo.bootstrap.Column
19772  * Bootstrap Column class
19773  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19774  * @cfg {Boolean} carousel true to make the group behave like a carousel
19775  * @cfg {Boolean} bullets show bullets for the panels
19776  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19777  * @cfg {Number} timer auto slide timer .. default 0 millisecond
19778  * @cfg {Boolean} showarrow (true|false) show arrow default true
19779  * 
19780  * @constructor
19781  * Create a new TabGroup
19782  * @param {Object} config The config object
19783  */
19784
19785 Roo.bootstrap.TabGroup = function(config){
19786     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19787     if (!this.navId) {
19788         this.navId = Roo.id();
19789     }
19790     this.tabs = [];
19791     Roo.bootstrap.TabGroup.register(this);
19792     
19793 };
19794
19795 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
19796     
19797     carousel : false,
19798     transition : false,
19799     bullets : 0,
19800     timer : 0,
19801     autoslide : false,
19802     slideFn : false,
19803     slideOnTouch : false,
19804     showarrow : true,
19805     
19806     getAutoCreate : function()
19807     {
19808         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19809         
19810         cfg.cls += ' tab-content';
19811         
19812         if (this.carousel) {
19813             cfg.cls += ' carousel slide';
19814             
19815             cfg.cn = [{
19816                cls : 'carousel-inner',
19817                cn : []
19818             }];
19819         
19820             if(this.bullets  && !Roo.isTouch){
19821                 
19822                 var bullets = {
19823                     cls : 'carousel-bullets',
19824                     cn : []
19825                 };
19826                
19827                 if(this.bullets_cls){
19828                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19829                 }
19830                 
19831                 bullets.cn.push({
19832                     cls : 'clear'
19833                 });
19834                 
19835                 cfg.cn[0].cn.push(bullets);
19836             }
19837             
19838             if(this.showarrow){
19839                 cfg.cn[0].cn.push({
19840                     tag : 'div',
19841                     class : 'carousel-arrow',
19842                     cn : [
19843                         {
19844                             tag : 'div',
19845                             class : 'carousel-prev',
19846                             cn : [
19847                                 {
19848                                     tag : 'i',
19849                                     class : 'fa fa-chevron-left'
19850                                 }
19851                             ]
19852                         },
19853                         {
19854                             tag : 'div',
19855                             class : 'carousel-next',
19856                             cn : [
19857                                 {
19858                                     tag : 'i',
19859                                     class : 'fa fa-chevron-right'
19860                                 }
19861                             ]
19862                         }
19863                     ]
19864                 });
19865             }
19866             
19867         }
19868         
19869         return cfg;
19870     },
19871     
19872     initEvents:  function()
19873     {
19874 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19875 //            this.el.on("touchstart", this.onTouchStart, this);
19876 //        }
19877         
19878         if(this.autoslide){
19879             var _this = this;
19880             
19881             this.slideFn = window.setInterval(function() {
19882                 _this.showPanelNext();
19883             }, this.timer);
19884         }
19885         
19886         if(this.showarrow){
19887             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
19888             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
19889         }
19890         
19891         
19892     },
19893     
19894 //    onTouchStart : function(e, el, o)
19895 //    {
19896 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
19897 //            return;
19898 //        }
19899 //        
19900 //        this.showPanelNext();
19901 //    },
19902     
19903     
19904     getChildContainer : function()
19905     {
19906         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
19907     },
19908     
19909     /**
19910     * register a Navigation item
19911     * @param {Roo.bootstrap.NavItem} the navitem to add
19912     */
19913     register : function(item)
19914     {
19915         this.tabs.push( item);
19916         item.navId = this.navId; // not really needed..
19917         this.addBullet();
19918     
19919     },
19920     
19921     getActivePanel : function()
19922     {
19923         var r = false;
19924         Roo.each(this.tabs, function(t) {
19925             if (t.active) {
19926                 r = t;
19927                 return false;
19928             }
19929             return null;
19930         });
19931         return r;
19932         
19933     },
19934     getPanelByName : function(n)
19935     {
19936         var r = false;
19937         Roo.each(this.tabs, function(t) {
19938             if (t.tabId == n) {
19939                 r = t;
19940                 return false;
19941             }
19942             return null;
19943         });
19944         return r;
19945     },
19946     indexOfPanel : function(p)
19947     {
19948         var r = false;
19949         Roo.each(this.tabs, function(t,i) {
19950             if (t.tabId == p.tabId) {
19951                 r = i;
19952                 return false;
19953             }
19954             return null;
19955         });
19956         return r;
19957     },
19958     /**
19959      * show a specific panel
19960      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
19961      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
19962      */
19963     showPanel : function (pan)
19964     {
19965         if(this.transition || typeof(pan) == 'undefined'){
19966             Roo.log("waiting for the transitionend");
19967             return false;
19968         }
19969         
19970         if (typeof(pan) == 'number') {
19971             pan = this.tabs[pan];
19972         }
19973         
19974         if (typeof(pan) == 'string') {
19975             pan = this.getPanelByName(pan);
19976         }
19977         
19978         var cur = this.getActivePanel();
19979         
19980         if(!pan || !cur){
19981             Roo.log('pan or acitve pan is undefined');
19982             return false;
19983         }
19984         
19985         if (pan.tabId == this.getActivePanel().tabId) {
19986             return true;
19987         }
19988         
19989         if (false === cur.fireEvent('beforedeactivate')) {
19990             return false;
19991         }
19992         
19993         if(this.bullets > 0 && !Roo.isTouch){
19994             this.setActiveBullet(this.indexOfPanel(pan));
19995         }
19996         
19997         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
19998             
19999             //class="carousel-item carousel-item-next carousel-item-left"
20000             
20001             this.transition = true;
20002             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20003             var lr = dir == 'next' ? 'left' : 'right';
20004             pan.el.addClass(dir); // or prev
20005             pan.el.addClass('carousel-item-' + dir); // or prev
20006             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20007             cur.el.addClass(lr); // or right
20008             pan.el.addClass(lr);
20009             cur.el.addClass('carousel-item-' +lr); // or right
20010             pan.el.addClass('carousel-item-' +lr);
20011             
20012             
20013             var _this = this;
20014             cur.el.on('transitionend', function() {
20015                 Roo.log("trans end?");
20016                 
20017                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20018                 pan.setActive(true);
20019                 
20020                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20021                 cur.setActive(false);
20022                 
20023                 _this.transition = false;
20024                 
20025             }, this, { single:  true } );
20026             
20027             return true;
20028         }
20029         
20030         cur.setActive(false);
20031         pan.setActive(true);
20032         
20033         return true;
20034         
20035     },
20036     showPanelNext : function()
20037     {
20038         var i = this.indexOfPanel(this.getActivePanel());
20039         
20040         if (i >= this.tabs.length - 1 && !this.autoslide) {
20041             return;
20042         }
20043         
20044         if (i >= this.tabs.length - 1 && this.autoslide) {
20045             i = -1;
20046         }
20047         
20048         this.showPanel(this.tabs[i+1]);
20049     },
20050     
20051     showPanelPrev : function()
20052     {
20053         var i = this.indexOfPanel(this.getActivePanel());
20054         
20055         if (i  < 1 && !this.autoslide) {
20056             return;
20057         }
20058         
20059         if (i < 1 && this.autoslide) {
20060             i = this.tabs.length;
20061         }
20062         
20063         this.showPanel(this.tabs[i-1]);
20064     },
20065     
20066     
20067     addBullet: function()
20068     {
20069         if(!this.bullets || Roo.isTouch){
20070             return;
20071         }
20072         var ctr = this.el.select('.carousel-bullets',true).first();
20073         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20074         var bullet = ctr.createChild({
20075             cls : 'bullet bullet-' + i
20076         },ctr.dom.lastChild);
20077         
20078         
20079         var _this = this;
20080         
20081         bullet.on('click', (function(e, el, o, ii, t){
20082
20083             e.preventDefault();
20084
20085             this.showPanel(ii);
20086
20087             if(this.autoslide && this.slideFn){
20088                 clearInterval(this.slideFn);
20089                 this.slideFn = window.setInterval(function() {
20090                     _this.showPanelNext();
20091                 }, this.timer);
20092             }
20093
20094         }).createDelegate(this, [i, bullet], true));
20095                 
20096         
20097     },
20098      
20099     setActiveBullet : function(i)
20100     {
20101         if(Roo.isTouch){
20102             return;
20103         }
20104         
20105         Roo.each(this.el.select('.bullet', true).elements, function(el){
20106             el.removeClass('selected');
20107         });
20108
20109         var bullet = this.el.select('.bullet-' + i, true).first();
20110         
20111         if(!bullet){
20112             return;
20113         }
20114         
20115         bullet.addClass('selected');
20116     }
20117     
20118     
20119   
20120 });
20121
20122  
20123
20124  
20125  
20126 Roo.apply(Roo.bootstrap.TabGroup, {
20127     
20128     groups: {},
20129      /**
20130     * register a Navigation Group
20131     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20132     */
20133     register : function(navgrp)
20134     {
20135         this.groups[navgrp.navId] = navgrp;
20136         
20137     },
20138     /**
20139     * fetch a Navigation Group based on the navigation ID
20140     * if one does not exist , it will get created.
20141     * @param {string} the navgroup to add
20142     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20143     */
20144     get: function(navId) {
20145         if (typeof(this.groups[navId]) == 'undefined') {
20146             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20147         }
20148         return this.groups[navId] ;
20149     }
20150     
20151     
20152     
20153 });
20154
20155  /*
20156  * - LGPL
20157  *
20158  * TabPanel
20159  * 
20160  */
20161
20162 /**
20163  * @class Roo.bootstrap.TabPanel
20164  * @extends Roo.bootstrap.Component
20165  * Bootstrap TabPanel class
20166  * @cfg {Boolean} active panel active
20167  * @cfg {String} html panel content
20168  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20169  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20170  * @cfg {String} href click to link..
20171  * 
20172  * 
20173  * @constructor
20174  * Create a new TabPanel
20175  * @param {Object} config The config object
20176  */
20177
20178 Roo.bootstrap.TabPanel = function(config){
20179     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20180     this.addEvents({
20181         /**
20182              * @event changed
20183              * Fires when the active status changes
20184              * @param {Roo.bootstrap.TabPanel} this
20185              * @param {Boolean} state the new state
20186             
20187          */
20188         'changed': true,
20189         /**
20190              * @event beforedeactivate
20191              * Fires before a tab is de-activated - can be used to do validation on a form.
20192              * @param {Roo.bootstrap.TabPanel} this
20193              * @return {Boolean} false if there is an error
20194             
20195          */
20196         'beforedeactivate': true
20197      });
20198     
20199     this.tabId = this.tabId || Roo.id();
20200   
20201 };
20202
20203 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20204     
20205     active: false,
20206     html: false,
20207     tabId: false,
20208     navId : false,
20209     href : '',
20210     
20211     getAutoCreate : function(){
20212         
20213         
20214         var cfg = {
20215             tag: 'div',
20216             // item is needed for carousel - not sure if it has any effect otherwise
20217             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20218             html: this.html || ''
20219         };
20220         
20221         if(this.active){
20222             cfg.cls += ' active';
20223         }
20224         
20225         if(this.tabId){
20226             cfg.tabId = this.tabId;
20227         }
20228         
20229         
20230         
20231         return cfg;
20232     },
20233     
20234     initEvents:  function()
20235     {
20236         var p = this.parent();
20237         
20238         this.navId = this.navId || p.navId;
20239         
20240         if (typeof(this.navId) != 'undefined') {
20241             // not really needed.. but just in case.. parent should be a NavGroup.
20242             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20243             
20244             tg.register(this);
20245             
20246             var i = tg.tabs.length - 1;
20247             
20248             if(this.active && tg.bullets > 0 && i < tg.bullets){
20249                 tg.setActiveBullet(i);
20250             }
20251         }
20252         
20253         this.el.on('click', this.onClick, this);
20254         
20255         if(Roo.isTouch){
20256             this.el.on("touchstart", this.onTouchStart, this);
20257             this.el.on("touchmove", this.onTouchMove, this);
20258             this.el.on("touchend", this.onTouchEnd, this);
20259         }
20260         
20261     },
20262     
20263     onRender : function(ct, position)
20264     {
20265         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20266     },
20267     
20268     setActive : function(state)
20269     {
20270         Roo.log("panel - set active " + this.tabId + "=" + state);
20271         
20272         this.active = state;
20273         if (!state) {
20274             this.el.removeClass('active');
20275             
20276         } else  if (!this.el.hasClass('active')) {
20277             this.el.addClass('active');
20278         }
20279         
20280         this.fireEvent('changed', this, state);
20281     },
20282     
20283     onClick : function(e)
20284     {
20285         e.preventDefault();
20286         
20287         if(!this.href.length){
20288             return;
20289         }
20290         
20291         window.location.href = this.href;
20292     },
20293     
20294     startX : 0,
20295     startY : 0,
20296     endX : 0,
20297     endY : 0,
20298     swiping : false,
20299     
20300     onTouchStart : function(e)
20301     {
20302         this.swiping = false;
20303         
20304         this.startX = e.browserEvent.touches[0].clientX;
20305         this.startY = e.browserEvent.touches[0].clientY;
20306     },
20307     
20308     onTouchMove : function(e)
20309     {
20310         this.swiping = true;
20311         
20312         this.endX = e.browserEvent.touches[0].clientX;
20313         this.endY = e.browserEvent.touches[0].clientY;
20314     },
20315     
20316     onTouchEnd : function(e)
20317     {
20318         if(!this.swiping){
20319             this.onClick(e);
20320             return;
20321         }
20322         
20323         var tabGroup = this.parent();
20324         
20325         if(this.endX > this.startX){ // swiping right
20326             tabGroup.showPanelPrev();
20327             return;
20328         }
20329         
20330         if(this.startX > this.endX){ // swiping left
20331             tabGroup.showPanelNext();
20332             return;
20333         }
20334     }
20335     
20336     
20337 });
20338  
20339
20340  
20341
20342  /*
20343  * - LGPL
20344  *
20345  * DateField
20346  * 
20347  */
20348
20349 /**
20350  * @class Roo.bootstrap.DateField
20351  * @extends Roo.bootstrap.Input
20352  * Bootstrap DateField class
20353  * @cfg {Number} weekStart default 0
20354  * @cfg {String} viewMode default empty, (months|years)
20355  * @cfg {String} minViewMode default empty, (months|years)
20356  * @cfg {Number} startDate default -Infinity
20357  * @cfg {Number} endDate default Infinity
20358  * @cfg {Boolean} todayHighlight default false
20359  * @cfg {Boolean} todayBtn default false
20360  * @cfg {Boolean} calendarWeeks default false
20361  * @cfg {Object} daysOfWeekDisabled default empty
20362  * @cfg {Boolean} singleMode default false (true | false)
20363  * 
20364  * @cfg {Boolean} keyboardNavigation default true
20365  * @cfg {String} language default en
20366  * 
20367  * @constructor
20368  * Create a new DateField
20369  * @param {Object} config The config object
20370  */
20371
20372 Roo.bootstrap.DateField = function(config){
20373     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20374      this.addEvents({
20375             /**
20376              * @event show
20377              * Fires when this field show.
20378              * @param {Roo.bootstrap.DateField} this
20379              * @param {Mixed} date The date value
20380              */
20381             show : true,
20382             /**
20383              * @event show
20384              * Fires when this field hide.
20385              * @param {Roo.bootstrap.DateField} this
20386              * @param {Mixed} date The date value
20387              */
20388             hide : true,
20389             /**
20390              * @event select
20391              * Fires when select a date.
20392              * @param {Roo.bootstrap.DateField} this
20393              * @param {Mixed} date The date value
20394              */
20395             select : true,
20396             /**
20397              * @event beforeselect
20398              * Fires when before select a date.
20399              * @param {Roo.bootstrap.DateField} this
20400              * @param {Mixed} date The date value
20401              */
20402             beforeselect : true
20403         });
20404 };
20405
20406 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20407     
20408     /**
20409      * @cfg {String} format
20410      * The default date format string which can be overriden for localization support.  The format must be
20411      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20412      */
20413     format : "m/d/y",
20414     /**
20415      * @cfg {String} altFormats
20416      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20417      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20418      */
20419     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20420     
20421     weekStart : 0,
20422     
20423     viewMode : '',
20424     
20425     minViewMode : '',
20426     
20427     todayHighlight : false,
20428     
20429     todayBtn: false,
20430     
20431     language: 'en',
20432     
20433     keyboardNavigation: true,
20434     
20435     calendarWeeks: false,
20436     
20437     startDate: -Infinity,
20438     
20439     endDate: Infinity,
20440     
20441     daysOfWeekDisabled: [],
20442     
20443     _events: [],
20444     
20445     singleMode : false,
20446     
20447     UTCDate: function()
20448     {
20449         return new Date(Date.UTC.apply(Date, arguments));
20450     },
20451     
20452     UTCToday: function()
20453     {
20454         var today = new Date();
20455         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20456     },
20457     
20458     getDate: function() {
20459             var d = this.getUTCDate();
20460             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20461     },
20462     
20463     getUTCDate: function() {
20464             return this.date;
20465     },
20466     
20467     setDate: function(d) {
20468             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20469     },
20470     
20471     setUTCDate: function(d) {
20472             this.date = d;
20473             this.setValue(this.formatDate(this.date));
20474     },
20475         
20476     onRender: function(ct, position)
20477     {
20478         
20479         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20480         
20481         this.language = this.language || 'en';
20482         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20483         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20484         
20485         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20486         this.format = this.format || 'm/d/y';
20487         this.isInline = false;
20488         this.isInput = true;
20489         this.component = this.el.select('.add-on', true).first() || false;
20490         this.component = (this.component && this.component.length === 0) ? false : this.component;
20491         this.hasInput = this.component && this.inputEl().length;
20492         
20493         if (typeof(this.minViewMode === 'string')) {
20494             switch (this.minViewMode) {
20495                 case 'months':
20496                     this.minViewMode = 1;
20497                     break;
20498                 case 'years':
20499                     this.minViewMode = 2;
20500                     break;
20501                 default:
20502                     this.minViewMode = 0;
20503                     break;
20504             }
20505         }
20506         
20507         if (typeof(this.viewMode === 'string')) {
20508             switch (this.viewMode) {
20509                 case 'months':
20510                     this.viewMode = 1;
20511                     break;
20512                 case 'years':
20513                     this.viewMode = 2;
20514                     break;
20515                 default:
20516                     this.viewMode = 0;
20517                     break;
20518             }
20519         }
20520                 
20521         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20522         
20523 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20524         
20525         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20526         
20527         this.picker().on('mousedown', this.onMousedown, this);
20528         this.picker().on('click', this.onClick, this);
20529         
20530         this.picker().addClass('datepicker-dropdown');
20531         
20532         this.startViewMode = this.viewMode;
20533         
20534         if(this.singleMode){
20535             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20536                 v.setVisibilityMode(Roo.Element.DISPLAY);
20537                 v.hide();
20538             });
20539             
20540             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20541                 v.setStyle('width', '189px');
20542             });
20543         }
20544         
20545         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20546             if(!this.calendarWeeks){
20547                 v.remove();
20548                 return;
20549             }
20550             
20551             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20552             v.attr('colspan', function(i, val){
20553                 return parseInt(val) + 1;
20554             });
20555         });
20556                         
20557         
20558         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20559         
20560         this.setStartDate(this.startDate);
20561         this.setEndDate(this.endDate);
20562         
20563         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20564         
20565         this.fillDow();
20566         this.fillMonths();
20567         this.update();
20568         this.showMode();
20569         
20570         if(this.isInline) {
20571             this.showPopup();
20572         }
20573     },
20574     
20575     picker : function()
20576     {
20577         return this.pickerEl;
20578 //        return this.el.select('.datepicker', true).first();
20579     },
20580     
20581     fillDow: function()
20582     {
20583         var dowCnt = this.weekStart;
20584         
20585         var dow = {
20586             tag: 'tr',
20587             cn: [
20588                 
20589             ]
20590         };
20591         
20592         if(this.calendarWeeks){
20593             dow.cn.push({
20594                 tag: 'th',
20595                 cls: 'cw',
20596                 html: '&nbsp;'
20597             })
20598         }
20599         
20600         while (dowCnt < this.weekStart + 7) {
20601             dow.cn.push({
20602                 tag: 'th',
20603                 cls: 'dow',
20604                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20605             });
20606         }
20607         
20608         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20609     },
20610     
20611     fillMonths: function()
20612     {    
20613         var i = 0;
20614         var months = this.picker().select('>.datepicker-months td', true).first();
20615         
20616         months.dom.innerHTML = '';
20617         
20618         while (i < 12) {
20619             var month = {
20620                 tag: 'span',
20621                 cls: 'month',
20622                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20623             };
20624             
20625             months.createChild(month);
20626         }
20627         
20628     },
20629     
20630     update: function()
20631     {
20632         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;
20633         
20634         if (this.date < this.startDate) {
20635             this.viewDate = new Date(this.startDate);
20636         } else if (this.date > this.endDate) {
20637             this.viewDate = new Date(this.endDate);
20638         } else {
20639             this.viewDate = new Date(this.date);
20640         }
20641         
20642         this.fill();
20643     },
20644     
20645     fill: function() 
20646     {
20647         var d = new Date(this.viewDate),
20648                 year = d.getUTCFullYear(),
20649                 month = d.getUTCMonth(),
20650                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20651                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20652                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20653                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20654                 currentDate = this.date && this.date.valueOf(),
20655                 today = this.UTCToday();
20656         
20657         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20658         
20659 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20660         
20661 //        this.picker.select('>tfoot th.today').
20662 //                                              .text(dates[this.language].today)
20663 //                                              .toggle(this.todayBtn !== false);
20664     
20665         this.updateNavArrows();
20666         this.fillMonths();
20667                                                 
20668         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20669         
20670         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20671          
20672         prevMonth.setUTCDate(day);
20673         
20674         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20675         
20676         var nextMonth = new Date(prevMonth);
20677         
20678         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20679         
20680         nextMonth = nextMonth.valueOf();
20681         
20682         var fillMonths = false;
20683         
20684         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20685         
20686         while(prevMonth.valueOf() <= nextMonth) {
20687             var clsName = '';
20688             
20689             if (prevMonth.getUTCDay() === this.weekStart) {
20690                 if(fillMonths){
20691                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20692                 }
20693                     
20694                 fillMonths = {
20695                     tag: 'tr',
20696                     cn: []
20697                 };
20698                 
20699                 if(this.calendarWeeks){
20700                     // ISO 8601: First week contains first thursday.
20701                     // ISO also states week starts on Monday, but we can be more abstract here.
20702                     var
20703                     // Start of current week: based on weekstart/current date
20704                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20705                     // Thursday of this week
20706                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20707                     // First Thursday of year, year from thursday
20708                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20709                     // Calendar week: ms between thursdays, div ms per day, div 7 days
20710                     calWeek =  (th - yth) / 864e5 / 7 + 1;
20711                     
20712                     fillMonths.cn.push({
20713                         tag: 'td',
20714                         cls: 'cw',
20715                         html: calWeek
20716                     });
20717                 }
20718             }
20719             
20720             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20721                 clsName += ' old';
20722             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20723                 clsName += ' new';
20724             }
20725             if (this.todayHighlight &&
20726                 prevMonth.getUTCFullYear() == today.getFullYear() &&
20727                 prevMonth.getUTCMonth() == today.getMonth() &&
20728                 prevMonth.getUTCDate() == today.getDate()) {
20729                 clsName += ' today';
20730             }
20731             
20732             if (currentDate && prevMonth.valueOf() === currentDate) {
20733                 clsName += ' active';
20734             }
20735             
20736             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20737                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20738                     clsName += ' disabled';
20739             }
20740             
20741             fillMonths.cn.push({
20742                 tag: 'td',
20743                 cls: 'day ' + clsName,
20744                 html: prevMonth.getDate()
20745             });
20746             
20747             prevMonth.setDate(prevMonth.getDate()+1);
20748         }
20749           
20750         var currentYear = this.date && this.date.getUTCFullYear();
20751         var currentMonth = this.date && this.date.getUTCMonth();
20752         
20753         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20754         
20755         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20756             v.removeClass('active');
20757             
20758             if(currentYear === year && k === currentMonth){
20759                 v.addClass('active');
20760             }
20761             
20762             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20763                 v.addClass('disabled');
20764             }
20765             
20766         });
20767         
20768         
20769         year = parseInt(year/10, 10) * 10;
20770         
20771         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20772         
20773         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20774         
20775         year -= 1;
20776         for (var i = -1; i < 11; i++) {
20777             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20778                 tag: 'span',
20779                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20780                 html: year
20781             });
20782             
20783             year += 1;
20784         }
20785     },
20786     
20787     showMode: function(dir) 
20788     {
20789         if (dir) {
20790             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20791         }
20792         
20793         Roo.each(this.picker().select('>div',true).elements, function(v){
20794             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20795             v.hide();
20796         });
20797         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20798     },
20799     
20800     place: function()
20801     {
20802         if(this.isInline) {
20803             return;
20804         }
20805         
20806         this.picker().removeClass(['bottom', 'top']);
20807         
20808         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20809             /*
20810              * place to the top of element!
20811              *
20812              */
20813             
20814             this.picker().addClass('top');
20815             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20816             
20817             return;
20818         }
20819         
20820         this.picker().addClass('bottom');
20821         
20822         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20823     },
20824     
20825     parseDate : function(value)
20826     {
20827         if(!value || value instanceof Date){
20828             return value;
20829         }
20830         var v = Date.parseDate(value, this.format);
20831         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20832             v = Date.parseDate(value, 'Y-m-d');
20833         }
20834         if(!v && this.altFormats){
20835             if(!this.altFormatsArray){
20836                 this.altFormatsArray = this.altFormats.split("|");
20837             }
20838             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20839                 v = Date.parseDate(value, this.altFormatsArray[i]);
20840             }
20841         }
20842         return v;
20843     },
20844     
20845     formatDate : function(date, fmt)
20846     {   
20847         return (!date || !(date instanceof Date)) ?
20848         date : date.dateFormat(fmt || this.format);
20849     },
20850     
20851     onFocus : function()
20852     {
20853         Roo.bootstrap.DateField.superclass.onFocus.call(this);
20854         this.showPopup();
20855     },
20856     
20857     onBlur : function()
20858     {
20859         Roo.bootstrap.DateField.superclass.onBlur.call(this);
20860         
20861         var d = this.inputEl().getValue();
20862         
20863         this.setValue(d);
20864                 
20865         this.hidePopup();
20866     },
20867     
20868     showPopup : function()
20869     {
20870         this.picker().show();
20871         this.update();
20872         this.place();
20873         
20874         this.fireEvent('showpopup', this, this.date);
20875     },
20876     
20877     hidePopup : function()
20878     {
20879         if(this.isInline) {
20880             return;
20881         }
20882         this.picker().hide();
20883         this.viewMode = this.startViewMode;
20884         this.showMode();
20885         
20886         this.fireEvent('hidepopup', this, this.date);
20887         
20888     },
20889     
20890     onMousedown: function(e)
20891     {
20892         e.stopPropagation();
20893         e.preventDefault();
20894     },
20895     
20896     keyup: function(e)
20897     {
20898         Roo.bootstrap.DateField.superclass.keyup.call(this);
20899         this.update();
20900     },
20901
20902     setValue: function(v)
20903     {
20904         if(this.fireEvent('beforeselect', this, v) !== false){
20905             var d = new Date(this.parseDate(v) ).clearTime();
20906         
20907             if(isNaN(d.getTime())){
20908                 this.date = this.viewDate = '';
20909                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20910                 return;
20911             }
20912
20913             v = this.formatDate(d);
20914
20915             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
20916
20917             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
20918
20919             this.update();
20920
20921             this.fireEvent('select', this, this.date);
20922         }
20923     },
20924     
20925     getValue: function()
20926     {
20927         return this.formatDate(this.date);
20928     },
20929     
20930     fireKey: function(e)
20931     {
20932         if (!this.picker().isVisible()){
20933             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20934                 this.showPopup();
20935             }
20936             return;
20937         }
20938         
20939         var dateChanged = false,
20940         dir, day, month,
20941         newDate, newViewDate;
20942         
20943         switch(e.keyCode){
20944             case 27: // escape
20945                 this.hidePopup();
20946                 e.preventDefault();
20947                 break;
20948             case 37: // left
20949             case 39: // right
20950                 if (!this.keyboardNavigation) {
20951                     break;
20952                 }
20953                 dir = e.keyCode == 37 ? -1 : 1;
20954                 
20955                 if (e.ctrlKey){
20956                     newDate = this.moveYear(this.date, dir);
20957                     newViewDate = this.moveYear(this.viewDate, dir);
20958                 } else if (e.shiftKey){
20959                     newDate = this.moveMonth(this.date, dir);
20960                     newViewDate = this.moveMonth(this.viewDate, dir);
20961                 } else {
20962                     newDate = new Date(this.date);
20963                     newDate.setUTCDate(this.date.getUTCDate() + dir);
20964                     newViewDate = new Date(this.viewDate);
20965                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
20966                 }
20967                 if (this.dateWithinRange(newDate)){
20968                     this.date = newDate;
20969                     this.viewDate = newViewDate;
20970                     this.setValue(this.formatDate(this.date));
20971 //                    this.update();
20972                     e.preventDefault();
20973                     dateChanged = true;
20974                 }
20975                 break;
20976             case 38: // up
20977             case 40: // down
20978                 if (!this.keyboardNavigation) {
20979                     break;
20980                 }
20981                 dir = e.keyCode == 38 ? -1 : 1;
20982                 if (e.ctrlKey){
20983                     newDate = this.moveYear(this.date, dir);
20984                     newViewDate = this.moveYear(this.viewDate, dir);
20985                 } else if (e.shiftKey){
20986                     newDate = this.moveMonth(this.date, dir);
20987                     newViewDate = this.moveMonth(this.viewDate, dir);
20988                 } else {
20989                     newDate = new Date(this.date);
20990                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
20991                     newViewDate = new Date(this.viewDate);
20992                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
20993                 }
20994                 if (this.dateWithinRange(newDate)){
20995                     this.date = newDate;
20996                     this.viewDate = newViewDate;
20997                     this.setValue(this.formatDate(this.date));
20998 //                    this.update();
20999                     e.preventDefault();
21000                     dateChanged = true;
21001                 }
21002                 break;
21003             case 13: // enter
21004                 this.setValue(this.formatDate(this.date));
21005                 this.hidePopup();
21006                 e.preventDefault();
21007                 break;
21008             case 9: // tab
21009                 this.setValue(this.formatDate(this.date));
21010                 this.hidePopup();
21011                 break;
21012             case 16: // shift
21013             case 17: // ctrl
21014             case 18: // alt
21015                 break;
21016             default :
21017                 this.hidePopup();
21018                 
21019         }
21020     },
21021     
21022     
21023     onClick: function(e) 
21024     {
21025         e.stopPropagation();
21026         e.preventDefault();
21027         
21028         var target = e.getTarget();
21029         
21030         if(target.nodeName.toLowerCase() === 'i'){
21031             target = Roo.get(target).dom.parentNode;
21032         }
21033         
21034         var nodeName = target.nodeName;
21035         var className = target.className;
21036         var html = target.innerHTML;
21037         //Roo.log(nodeName);
21038         
21039         switch(nodeName.toLowerCase()) {
21040             case 'th':
21041                 switch(className) {
21042                     case 'switch':
21043                         this.showMode(1);
21044                         break;
21045                     case 'prev':
21046                     case 'next':
21047                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21048                         switch(this.viewMode){
21049                                 case 0:
21050                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21051                                         break;
21052                                 case 1:
21053                                 case 2:
21054                                         this.viewDate = this.moveYear(this.viewDate, dir);
21055                                         break;
21056                         }
21057                         this.fill();
21058                         break;
21059                     case 'today':
21060                         var date = new Date();
21061                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21062 //                        this.fill()
21063                         this.setValue(this.formatDate(this.date));
21064                         
21065                         this.hidePopup();
21066                         break;
21067                 }
21068                 break;
21069             case 'span':
21070                 if (className.indexOf('disabled') < 0) {
21071                     this.viewDate.setUTCDate(1);
21072                     if (className.indexOf('month') > -1) {
21073                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21074                     } else {
21075                         var year = parseInt(html, 10) || 0;
21076                         this.viewDate.setUTCFullYear(year);
21077                         
21078                     }
21079                     
21080                     if(this.singleMode){
21081                         this.setValue(this.formatDate(this.viewDate));
21082                         this.hidePopup();
21083                         return;
21084                     }
21085                     
21086                     this.showMode(-1);
21087                     this.fill();
21088                 }
21089                 break;
21090                 
21091             case 'td':
21092                 //Roo.log(className);
21093                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21094                     var day = parseInt(html, 10) || 1;
21095                     var year = this.viewDate.getUTCFullYear(),
21096                         month = this.viewDate.getUTCMonth();
21097
21098                     if (className.indexOf('old') > -1) {
21099                         if(month === 0 ){
21100                             month = 11;
21101                             year -= 1;
21102                         }else{
21103                             month -= 1;
21104                         }
21105                     } else if (className.indexOf('new') > -1) {
21106                         if (month == 11) {
21107                             month = 0;
21108                             year += 1;
21109                         } else {
21110                             month += 1;
21111                         }
21112                     }
21113                     //Roo.log([year,month,day]);
21114                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21115                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21116 //                    this.fill();
21117                     //Roo.log(this.formatDate(this.date));
21118                     this.setValue(this.formatDate(this.date));
21119                     this.hidePopup();
21120                 }
21121                 break;
21122         }
21123     },
21124     
21125     setStartDate: function(startDate)
21126     {
21127         this.startDate = startDate || -Infinity;
21128         if (this.startDate !== -Infinity) {
21129             this.startDate = this.parseDate(this.startDate);
21130         }
21131         this.update();
21132         this.updateNavArrows();
21133     },
21134
21135     setEndDate: function(endDate)
21136     {
21137         this.endDate = endDate || Infinity;
21138         if (this.endDate !== Infinity) {
21139             this.endDate = this.parseDate(this.endDate);
21140         }
21141         this.update();
21142         this.updateNavArrows();
21143     },
21144     
21145     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21146     {
21147         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21148         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21149             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21150         }
21151         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21152             return parseInt(d, 10);
21153         });
21154         this.update();
21155         this.updateNavArrows();
21156     },
21157     
21158     updateNavArrows: function() 
21159     {
21160         if(this.singleMode){
21161             return;
21162         }
21163         
21164         var d = new Date(this.viewDate),
21165         year = d.getUTCFullYear(),
21166         month = d.getUTCMonth();
21167         
21168         Roo.each(this.picker().select('.prev', true).elements, function(v){
21169             v.show();
21170             switch (this.viewMode) {
21171                 case 0:
21172
21173                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21174                         v.hide();
21175                     }
21176                     break;
21177                 case 1:
21178                 case 2:
21179                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21180                         v.hide();
21181                     }
21182                     break;
21183             }
21184         });
21185         
21186         Roo.each(this.picker().select('.next', true).elements, function(v){
21187             v.show();
21188             switch (this.viewMode) {
21189                 case 0:
21190
21191                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21192                         v.hide();
21193                     }
21194                     break;
21195                 case 1:
21196                 case 2:
21197                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21198                         v.hide();
21199                     }
21200                     break;
21201             }
21202         })
21203     },
21204     
21205     moveMonth: function(date, dir)
21206     {
21207         if (!dir) {
21208             return date;
21209         }
21210         var new_date = new Date(date.valueOf()),
21211         day = new_date.getUTCDate(),
21212         month = new_date.getUTCMonth(),
21213         mag = Math.abs(dir),
21214         new_month, test;
21215         dir = dir > 0 ? 1 : -1;
21216         if (mag == 1){
21217             test = dir == -1
21218             // If going back one month, make sure month is not current month
21219             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21220             ? function(){
21221                 return new_date.getUTCMonth() == month;
21222             }
21223             // If going forward one month, make sure month is as expected
21224             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21225             : function(){
21226                 return new_date.getUTCMonth() != new_month;
21227             };
21228             new_month = month + dir;
21229             new_date.setUTCMonth(new_month);
21230             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21231             if (new_month < 0 || new_month > 11) {
21232                 new_month = (new_month + 12) % 12;
21233             }
21234         } else {
21235             // For magnitudes >1, move one month at a time...
21236             for (var i=0; i<mag; i++) {
21237                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21238                 new_date = this.moveMonth(new_date, dir);
21239             }
21240             // ...then reset the day, keeping it in the new month
21241             new_month = new_date.getUTCMonth();
21242             new_date.setUTCDate(day);
21243             test = function(){
21244                 return new_month != new_date.getUTCMonth();
21245             };
21246         }
21247         // Common date-resetting loop -- if date is beyond end of month, make it
21248         // end of month
21249         while (test()){
21250             new_date.setUTCDate(--day);
21251             new_date.setUTCMonth(new_month);
21252         }
21253         return new_date;
21254     },
21255
21256     moveYear: function(date, dir)
21257     {
21258         return this.moveMonth(date, dir*12);
21259     },
21260
21261     dateWithinRange: function(date)
21262     {
21263         return date >= this.startDate && date <= this.endDate;
21264     },
21265
21266     
21267     remove: function() 
21268     {
21269         this.picker().remove();
21270     },
21271     
21272     validateValue : function(value)
21273     {
21274         if(this.getVisibilityEl().hasClass('hidden')){
21275             return true;
21276         }
21277         
21278         if(value.length < 1)  {
21279             if(this.allowBlank){
21280                 return true;
21281             }
21282             return false;
21283         }
21284         
21285         if(value.length < this.minLength){
21286             return false;
21287         }
21288         if(value.length > this.maxLength){
21289             return false;
21290         }
21291         if(this.vtype){
21292             var vt = Roo.form.VTypes;
21293             if(!vt[this.vtype](value, this)){
21294                 return false;
21295             }
21296         }
21297         if(typeof this.validator == "function"){
21298             var msg = this.validator(value);
21299             if(msg !== true){
21300                 return false;
21301             }
21302         }
21303         
21304         if(this.regex && !this.regex.test(value)){
21305             return false;
21306         }
21307         
21308         if(typeof(this.parseDate(value)) == 'undefined'){
21309             return false;
21310         }
21311         
21312         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21313             return false;
21314         }      
21315         
21316         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21317             return false;
21318         } 
21319         
21320         
21321         return true;
21322     },
21323     
21324     reset : function()
21325     {
21326         this.date = this.viewDate = '';
21327         
21328         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21329     }
21330    
21331 });
21332
21333 Roo.apply(Roo.bootstrap.DateField,  {
21334     
21335     head : {
21336         tag: 'thead',
21337         cn: [
21338         {
21339             tag: 'tr',
21340             cn: [
21341             {
21342                 tag: 'th',
21343                 cls: 'prev',
21344                 html: '<i class="fa fa-arrow-left"/>'
21345             },
21346             {
21347                 tag: 'th',
21348                 cls: 'switch',
21349                 colspan: '5'
21350             },
21351             {
21352                 tag: 'th',
21353                 cls: 'next',
21354                 html: '<i class="fa fa-arrow-right"/>'
21355             }
21356
21357             ]
21358         }
21359         ]
21360     },
21361     
21362     content : {
21363         tag: 'tbody',
21364         cn: [
21365         {
21366             tag: 'tr',
21367             cn: [
21368             {
21369                 tag: 'td',
21370                 colspan: '7'
21371             }
21372             ]
21373         }
21374         ]
21375     },
21376     
21377     footer : {
21378         tag: 'tfoot',
21379         cn: [
21380         {
21381             tag: 'tr',
21382             cn: [
21383             {
21384                 tag: 'th',
21385                 colspan: '7',
21386                 cls: 'today'
21387             }
21388                     
21389             ]
21390         }
21391         ]
21392     },
21393     
21394     dates:{
21395         en: {
21396             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21397             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21398             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21399             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21400             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21401             today: "Today"
21402         }
21403     },
21404     
21405     modes: [
21406     {
21407         clsName: 'days',
21408         navFnc: 'Month',
21409         navStep: 1
21410     },
21411     {
21412         clsName: 'months',
21413         navFnc: 'FullYear',
21414         navStep: 1
21415     },
21416     {
21417         clsName: 'years',
21418         navFnc: 'FullYear',
21419         navStep: 10
21420     }]
21421 });
21422
21423 Roo.apply(Roo.bootstrap.DateField,  {
21424   
21425     template : {
21426         tag: 'div',
21427         cls: 'datepicker dropdown-menu roo-dynamic',
21428         cn: [
21429         {
21430             tag: 'div',
21431             cls: 'datepicker-days',
21432             cn: [
21433             {
21434                 tag: 'table',
21435                 cls: 'table-condensed',
21436                 cn:[
21437                 Roo.bootstrap.DateField.head,
21438                 {
21439                     tag: 'tbody'
21440                 },
21441                 Roo.bootstrap.DateField.footer
21442                 ]
21443             }
21444             ]
21445         },
21446         {
21447             tag: 'div',
21448             cls: 'datepicker-months',
21449             cn: [
21450             {
21451                 tag: 'table',
21452                 cls: 'table-condensed',
21453                 cn:[
21454                 Roo.bootstrap.DateField.head,
21455                 Roo.bootstrap.DateField.content,
21456                 Roo.bootstrap.DateField.footer
21457                 ]
21458             }
21459             ]
21460         },
21461         {
21462             tag: 'div',
21463             cls: 'datepicker-years',
21464             cn: [
21465             {
21466                 tag: 'table',
21467                 cls: 'table-condensed',
21468                 cn:[
21469                 Roo.bootstrap.DateField.head,
21470                 Roo.bootstrap.DateField.content,
21471                 Roo.bootstrap.DateField.footer
21472                 ]
21473             }
21474             ]
21475         }
21476         ]
21477     }
21478 });
21479
21480  
21481
21482  /*
21483  * - LGPL
21484  *
21485  * TimeField
21486  * 
21487  */
21488
21489 /**
21490  * @class Roo.bootstrap.TimeField
21491  * @extends Roo.bootstrap.Input
21492  * Bootstrap DateField class
21493  * 
21494  * 
21495  * @constructor
21496  * Create a new TimeField
21497  * @param {Object} config The config object
21498  */
21499
21500 Roo.bootstrap.TimeField = function(config){
21501     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21502     this.addEvents({
21503             /**
21504              * @event show
21505              * Fires when this field show.
21506              * @param {Roo.bootstrap.DateField} thisthis
21507              * @param {Mixed} date The date value
21508              */
21509             show : true,
21510             /**
21511              * @event show
21512              * Fires when this field hide.
21513              * @param {Roo.bootstrap.DateField} this
21514              * @param {Mixed} date The date value
21515              */
21516             hide : true,
21517             /**
21518              * @event select
21519              * Fires when select a date.
21520              * @param {Roo.bootstrap.DateField} this
21521              * @param {Mixed} date The date value
21522              */
21523             select : true
21524         });
21525 };
21526
21527 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21528     
21529     /**
21530      * @cfg {String} format
21531      * The default time format string which can be overriden for localization support.  The format must be
21532      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21533      */
21534     format : "H:i",
21535        
21536     onRender: function(ct, position)
21537     {
21538         
21539         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21540                 
21541         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21542         
21543         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21544         
21545         this.pop = this.picker().select('>.datepicker-time',true).first();
21546         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21547         
21548         this.picker().on('mousedown', this.onMousedown, this);
21549         this.picker().on('click', this.onClick, this);
21550         
21551         this.picker().addClass('datepicker-dropdown');
21552     
21553         this.fillTime();
21554         this.update();
21555             
21556         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21557         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21558         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21559         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21560         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21561         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21562
21563     },
21564     
21565     fireKey: function(e){
21566         if (!this.picker().isVisible()){
21567             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21568                 this.show();
21569             }
21570             return;
21571         }
21572
21573         e.preventDefault();
21574         
21575         switch(e.keyCode){
21576             case 27: // escape
21577                 this.hide();
21578                 break;
21579             case 37: // left
21580             case 39: // right
21581                 this.onTogglePeriod();
21582                 break;
21583             case 38: // up
21584                 this.onIncrementMinutes();
21585                 break;
21586             case 40: // down
21587                 this.onDecrementMinutes();
21588                 break;
21589             case 13: // enter
21590             case 9: // tab
21591                 this.setTime();
21592                 break;
21593         }
21594     },
21595     
21596     onClick: function(e) {
21597         e.stopPropagation();
21598         e.preventDefault();
21599     },
21600     
21601     picker : function()
21602     {
21603         return this.el.select('.datepicker', true).first();
21604     },
21605     
21606     fillTime: function()
21607     {    
21608         var time = this.pop.select('tbody', true).first();
21609         
21610         time.dom.innerHTML = '';
21611         
21612         time.createChild({
21613             tag: 'tr',
21614             cn: [
21615                 {
21616                     tag: 'td',
21617                     cn: [
21618                         {
21619                             tag: 'a',
21620                             href: '#',
21621                             cls: 'btn',
21622                             cn: [
21623                                 {
21624                                     tag: 'span',
21625                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
21626                                 }
21627                             ]
21628                         } 
21629                     ]
21630                 },
21631                 {
21632                     tag: 'td',
21633                     cls: 'separator'
21634                 },
21635                 {
21636                     tag: 'td',
21637                     cn: [
21638                         {
21639                             tag: 'a',
21640                             href: '#',
21641                             cls: 'btn',
21642                             cn: [
21643                                 {
21644                                     tag: 'span',
21645                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
21646                                 }
21647                             ]
21648                         }
21649                     ]
21650                 },
21651                 {
21652                     tag: 'td',
21653                     cls: 'separator'
21654                 }
21655             ]
21656         });
21657         
21658         time.createChild({
21659             tag: 'tr',
21660             cn: [
21661                 {
21662                     tag: 'td',
21663                     cn: [
21664                         {
21665                             tag: 'span',
21666                             cls: 'timepicker-hour',
21667                             html: '00'
21668                         }  
21669                     ]
21670                 },
21671                 {
21672                     tag: 'td',
21673                     cls: 'separator',
21674                     html: ':'
21675                 },
21676                 {
21677                     tag: 'td',
21678                     cn: [
21679                         {
21680                             tag: 'span',
21681                             cls: 'timepicker-minute',
21682                             html: '00'
21683                         }  
21684                     ]
21685                 },
21686                 {
21687                     tag: 'td',
21688                     cls: 'separator'
21689                 },
21690                 {
21691                     tag: 'td',
21692                     cn: [
21693                         {
21694                             tag: 'button',
21695                             type: 'button',
21696                             cls: 'btn btn-primary period',
21697                             html: 'AM'
21698                             
21699                         }
21700                     ]
21701                 }
21702             ]
21703         });
21704         
21705         time.createChild({
21706             tag: 'tr',
21707             cn: [
21708                 {
21709                     tag: 'td',
21710                     cn: [
21711                         {
21712                             tag: 'a',
21713                             href: '#',
21714                             cls: 'btn',
21715                             cn: [
21716                                 {
21717                                     tag: 'span',
21718                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
21719                                 }
21720                             ]
21721                         }
21722                     ]
21723                 },
21724                 {
21725                     tag: 'td',
21726                     cls: 'separator'
21727                 },
21728                 {
21729                     tag: 'td',
21730                     cn: [
21731                         {
21732                             tag: 'a',
21733                             href: '#',
21734                             cls: 'btn',
21735                             cn: [
21736                                 {
21737                                     tag: 'span',
21738                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
21739                                 }
21740                             ]
21741                         }
21742                     ]
21743                 },
21744                 {
21745                     tag: 'td',
21746                     cls: 'separator'
21747                 }
21748             ]
21749         });
21750         
21751     },
21752     
21753     update: function()
21754     {
21755         
21756         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21757         
21758         this.fill();
21759     },
21760     
21761     fill: function() 
21762     {
21763         var hours = this.time.getHours();
21764         var minutes = this.time.getMinutes();
21765         var period = 'AM';
21766         
21767         if(hours > 11){
21768             period = 'PM';
21769         }
21770         
21771         if(hours == 0){
21772             hours = 12;
21773         }
21774         
21775         
21776         if(hours > 12){
21777             hours = hours - 12;
21778         }
21779         
21780         if(hours < 10){
21781             hours = '0' + hours;
21782         }
21783         
21784         if(minutes < 10){
21785             minutes = '0' + minutes;
21786         }
21787         
21788         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21789         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21790         this.pop.select('button', true).first().dom.innerHTML = period;
21791         
21792     },
21793     
21794     place: function()
21795     {   
21796         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21797         
21798         var cls = ['bottom'];
21799         
21800         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21801             cls.pop();
21802             cls.push('top');
21803         }
21804         
21805         cls.push('right');
21806         
21807         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21808             cls.pop();
21809             cls.push('left');
21810         }
21811         
21812         this.picker().addClass(cls.join('-'));
21813         
21814         var _this = this;
21815         
21816         Roo.each(cls, function(c){
21817             if(c == 'bottom'){
21818                 _this.picker().setTop(_this.inputEl().getHeight());
21819                 return;
21820             }
21821             if(c == 'top'){
21822                 _this.picker().setTop(0 - _this.picker().getHeight());
21823                 return;
21824             }
21825             
21826             if(c == 'left'){
21827                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21828                 return;
21829             }
21830             if(c == 'right'){
21831                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21832                 return;
21833             }
21834         });
21835         
21836     },
21837   
21838     onFocus : function()
21839     {
21840         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21841         this.show();
21842     },
21843     
21844     onBlur : function()
21845     {
21846         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21847         this.hide();
21848     },
21849     
21850     show : function()
21851     {
21852         this.picker().show();
21853         this.pop.show();
21854         this.update();
21855         this.place();
21856         
21857         this.fireEvent('show', this, this.date);
21858     },
21859     
21860     hide : function()
21861     {
21862         this.picker().hide();
21863         this.pop.hide();
21864         
21865         this.fireEvent('hide', this, this.date);
21866     },
21867     
21868     setTime : function()
21869     {
21870         this.hide();
21871         this.setValue(this.time.format(this.format));
21872         
21873         this.fireEvent('select', this, this.date);
21874         
21875         
21876     },
21877     
21878     onMousedown: function(e){
21879         e.stopPropagation();
21880         e.preventDefault();
21881     },
21882     
21883     onIncrementHours: function()
21884     {
21885         Roo.log('onIncrementHours');
21886         this.time = this.time.add(Date.HOUR, 1);
21887         this.update();
21888         
21889     },
21890     
21891     onDecrementHours: function()
21892     {
21893         Roo.log('onDecrementHours');
21894         this.time = this.time.add(Date.HOUR, -1);
21895         this.update();
21896     },
21897     
21898     onIncrementMinutes: function()
21899     {
21900         Roo.log('onIncrementMinutes');
21901         this.time = this.time.add(Date.MINUTE, 1);
21902         this.update();
21903     },
21904     
21905     onDecrementMinutes: function()
21906     {
21907         Roo.log('onDecrementMinutes');
21908         this.time = this.time.add(Date.MINUTE, -1);
21909         this.update();
21910     },
21911     
21912     onTogglePeriod: function()
21913     {
21914         Roo.log('onTogglePeriod');
21915         this.time = this.time.add(Date.HOUR, 12);
21916         this.update();
21917     }
21918     
21919    
21920 });
21921
21922 Roo.apply(Roo.bootstrap.TimeField,  {
21923     
21924     content : {
21925         tag: 'tbody',
21926         cn: [
21927             {
21928                 tag: 'tr',
21929                 cn: [
21930                 {
21931                     tag: 'td',
21932                     colspan: '7'
21933                 }
21934                 ]
21935             }
21936         ]
21937     },
21938     
21939     footer : {
21940         tag: 'tfoot',
21941         cn: [
21942             {
21943                 tag: 'tr',
21944                 cn: [
21945                 {
21946                     tag: 'th',
21947                     colspan: '7',
21948                     cls: '',
21949                     cn: [
21950                         {
21951                             tag: 'button',
21952                             cls: 'btn btn-info ok',
21953                             html: 'OK'
21954                         }
21955                     ]
21956                 }
21957
21958                 ]
21959             }
21960         ]
21961     }
21962 });
21963
21964 Roo.apply(Roo.bootstrap.TimeField,  {
21965   
21966     template : {
21967         tag: 'div',
21968         cls: 'datepicker dropdown-menu',
21969         cn: [
21970             {
21971                 tag: 'div',
21972                 cls: 'datepicker-time',
21973                 cn: [
21974                 {
21975                     tag: 'table',
21976                     cls: 'table-condensed',
21977                     cn:[
21978                     Roo.bootstrap.TimeField.content,
21979                     Roo.bootstrap.TimeField.footer
21980                     ]
21981                 }
21982                 ]
21983             }
21984         ]
21985     }
21986 });
21987
21988  
21989
21990  /*
21991  * - LGPL
21992  *
21993  * MonthField
21994  * 
21995  */
21996
21997 /**
21998  * @class Roo.bootstrap.MonthField
21999  * @extends Roo.bootstrap.Input
22000  * Bootstrap MonthField class
22001  * 
22002  * @cfg {String} language default en
22003  * 
22004  * @constructor
22005  * Create a new MonthField
22006  * @param {Object} config The config object
22007  */
22008
22009 Roo.bootstrap.MonthField = function(config){
22010     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22011     
22012     this.addEvents({
22013         /**
22014          * @event show
22015          * Fires when this field show.
22016          * @param {Roo.bootstrap.MonthField} this
22017          * @param {Mixed} date The date value
22018          */
22019         show : true,
22020         /**
22021          * @event show
22022          * Fires when this field hide.
22023          * @param {Roo.bootstrap.MonthField} this
22024          * @param {Mixed} date The date value
22025          */
22026         hide : true,
22027         /**
22028          * @event select
22029          * Fires when select a date.
22030          * @param {Roo.bootstrap.MonthField} this
22031          * @param {String} oldvalue The old value
22032          * @param {String} newvalue The new value
22033          */
22034         select : true
22035     });
22036 };
22037
22038 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22039     
22040     onRender: function(ct, position)
22041     {
22042         
22043         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22044         
22045         this.language = this.language || 'en';
22046         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22047         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22048         
22049         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22050         this.isInline = false;
22051         this.isInput = true;
22052         this.component = this.el.select('.add-on', true).first() || false;
22053         this.component = (this.component && this.component.length === 0) ? false : this.component;
22054         this.hasInput = this.component && this.inputEL().length;
22055         
22056         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22057         
22058         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22059         
22060         this.picker().on('mousedown', this.onMousedown, this);
22061         this.picker().on('click', this.onClick, this);
22062         
22063         this.picker().addClass('datepicker-dropdown');
22064         
22065         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22066             v.setStyle('width', '189px');
22067         });
22068         
22069         this.fillMonths();
22070         
22071         this.update();
22072         
22073         if(this.isInline) {
22074             this.show();
22075         }
22076         
22077     },
22078     
22079     setValue: function(v, suppressEvent)
22080     {   
22081         var o = this.getValue();
22082         
22083         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22084         
22085         this.update();
22086
22087         if(suppressEvent !== true){
22088             this.fireEvent('select', this, o, v);
22089         }
22090         
22091     },
22092     
22093     getValue: function()
22094     {
22095         return this.value;
22096     },
22097     
22098     onClick: function(e) 
22099     {
22100         e.stopPropagation();
22101         e.preventDefault();
22102         
22103         var target = e.getTarget();
22104         
22105         if(target.nodeName.toLowerCase() === 'i'){
22106             target = Roo.get(target).dom.parentNode;
22107         }
22108         
22109         var nodeName = target.nodeName;
22110         var className = target.className;
22111         var html = target.innerHTML;
22112         
22113         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22114             return;
22115         }
22116         
22117         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22118         
22119         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22120         
22121         this.hide();
22122                         
22123     },
22124     
22125     picker : function()
22126     {
22127         return this.pickerEl;
22128     },
22129     
22130     fillMonths: function()
22131     {    
22132         var i = 0;
22133         var months = this.picker().select('>.datepicker-months td', true).first();
22134         
22135         months.dom.innerHTML = '';
22136         
22137         while (i < 12) {
22138             var month = {
22139                 tag: 'span',
22140                 cls: 'month',
22141                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22142             };
22143             
22144             months.createChild(month);
22145         }
22146         
22147     },
22148     
22149     update: function()
22150     {
22151         var _this = this;
22152         
22153         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22154             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22155         }
22156         
22157         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22158             e.removeClass('active');
22159             
22160             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22161                 e.addClass('active');
22162             }
22163         })
22164     },
22165     
22166     place: function()
22167     {
22168         if(this.isInline) {
22169             return;
22170         }
22171         
22172         this.picker().removeClass(['bottom', 'top']);
22173         
22174         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22175             /*
22176              * place to the top of element!
22177              *
22178              */
22179             
22180             this.picker().addClass('top');
22181             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22182             
22183             return;
22184         }
22185         
22186         this.picker().addClass('bottom');
22187         
22188         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22189     },
22190     
22191     onFocus : function()
22192     {
22193         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22194         this.show();
22195     },
22196     
22197     onBlur : function()
22198     {
22199         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22200         
22201         var d = this.inputEl().getValue();
22202         
22203         this.setValue(d);
22204                 
22205         this.hide();
22206     },
22207     
22208     show : function()
22209     {
22210         this.picker().show();
22211         this.picker().select('>.datepicker-months', true).first().show();
22212         this.update();
22213         this.place();
22214         
22215         this.fireEvent('show', this, this.date);
22216     },
22217     
22218     hide : function()
22219     {
22220         if(this.isInline) {
22221             return;
22222         }
22223         this.picker().hide();
22224         this.fireEvent('hide', this, this.date);
22225         
22226     },
22227     
22228     onMousedown: function(e)
22229     {
22230         e.stopPropagation();
22231         e.preventDefault();
22232     },
22233     
22234     keyup: function(e)
22235     {
22236         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22237         this.update();
22238     },
22239
22240     fireKey: function(e)
22241     {
22242         if (!this.picker().isVisible()){
22243             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22244                 this.show();
22245             }
22246             return;
22247         }
22248         
22249         var dir;
22250         
22251         switch(e.keyCode){
22252             case 27: // escape
22253                 this.hide();
22254                 e.preventDefault();
22255                 break;
22256             case 37: // left
22257             case 39: // right
22258                 dir = e.keyCode == 37 ? -1 : 1;
22259                 
22260                 this.vIndex = this.vIndex + dir;
22261                 
22262                 if(this.vIndex < 0){
22263                     this.vIndex = 0;
22264                 }
22265                 
22266                 if(this.vIndex > 11){
22267                     this.vIndex = 11;
22268                 }
22269                 
22270                 if(isNaN(this.vIndex)){
22271                     this.vIndex = 0;
22272                 }
22273                 
22274                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22275                 
22276                 break;
22277             case 38: // up
22278             case 40: // down
22279                 
22280                 dir = e.keyCode == 38 ? -1 : 1;
22281                 
22282                 this.vIndex = this.vIndex + dir * 4;
22283                 
22284                 if(this.vIndex < 0){
22285                     this.vIndex = 0;
22286                 }
22287                 
22288                 if(this.vIndex > 11){
22289                     this.vIndex = 11;
22290                 }
22291                 
22292                 if(isNaN(this.vIndex)){
22293                     this.vIndex = 0;
22294                 }
22295                 
22296                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22297                 break;
22298                 
22299             case 13: // enter
22300                 
22301                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22302                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22303                 }
22304                 
22305                 this.hide();
22306                 e.preventDefault();
22307                 break;
22308             case 9: // tab
22309                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22310                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22311                 }
22312                 this.hide();
22313                 break;
22314             case 16: // shift
22315             case 17: // ctrl
22316             case 18: // alt
22317                 break;
22318             default :
22319                 this.hide();
22320                 
22321         }
22322     },
22323     
22324     remove: function() 
22325     {
22326         this.picker().remove();
22327     }
22328    
22329 });
22330
22331 Roo.apply(Roo.bootstrap.MonthField,  {
22332     
22333     content : {
22334         tag: 'tbody',
22335         cn: [
22336         {
22337             tag: 'tr',
22338             cn: [
22339             {
22340                 tag: 'td',
22341                 colspan: '7'
22342             }
22343             ]
22344         }
22345         ]
22346     },
22347     
22348     dates:{
22349         en: {
22350             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22351             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22352         }
22353     }
22354 });
22355
22356 Roo.apply(Roo.bootstrap.MonthField,  {
22357   
22358     template : {
22359         tag: 'div',
22360         cls: 'datepicker dropdown-menu roo-dynamic',
22361         cn: [
22362             {
22363                 tag: 'div',
22364                 cls: 'datepicker-months',
22365                 cn: [
22366                 {
22367                     tag: 'table',
22368                     cls: 'table-condensed',
22369                     cn:[
22370                         Roo.bootstrap.DateField.content
22371                     ]
22372                 }
22373                 ]
22374             }
22375         ]
22376     }
22377 });
22378
22379  
22380
22381  
22382  /*
22383  * - LGPL
22384  *
22385  * CheckBox
22386  * 
22387  */
22388
22389 /**
22390  * @class Roo.bootstrap.CheckBox
22391  * @extends Roo.bootstrap.Input
22392  * Bootstrap CheckBox class
22393  * 
22394  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22395  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22396  * @cfg {String} boxLabel The text that appears beside the checkbox
22397  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22398  * @cfg {Boolean} checked initnal the element
22399  * @cfg {Boolean} inline inline the element (default false)
22400  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22401  * @cfg {String} tooltip label tooltip
22402  * 
22403  * @constructor
22404  * Create a new CheckBox
22405  * @param {Object} config The config object
22406  */
22407
22408 Roo.bootstrap.CheckBox = function(config){
22409     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22410    
22411     this.addEvents({
22412         /**
22413         * @event check
22414         * Fires when the element is checked or unchecked.
22415         * @param {Roo.bootstrap.CheckBox} this This input
22416         * @param {Boolean} checked The new checked value
22417         */
22418        check : true,
22419        /**
22420         * @event click
22421         * Fires when the element is click.
22422         * @param {Roo.bootstrap.CheckBox} this This input
22423         */
22424        click : true
22425     });
22426     
22427 };
22428
22429 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22430   
22431     inputType: 'checkbox',
22432     inputValue: 1,
22433     valueOff: 0,
22434     boxLabel: false,
22435     checked: false,
22436     weight : false,
22437     inline: false,
22438     tooltip : '',
22439     
22440     // checkbox success does not make any sense really.. 
22441     invalidClass : "",
22442     validClass : "",
22443     
22444     
22445     getAutoCreate : function()
22446     {
22447         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22448         
22449         var id = Roo.id();
22450         
22451         var cfg = {};
22452         
22453         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22454         
22455         if(this.inline){
22456             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22457         }
22458         
22459         var input =  {
22460             tag: 'input',
22461             id : id,
22462             type : this.inputType,
22463             value : this.inputValue,
22464             cls : 'roo-' + this.inputType, //'form-box',
22465             placeholder : this.placeholder || ''
22466             
22467         };
22468         
22469         if(this.inputType != 'radio'){
22470             var hidden =  {
22471                 tag: 'input',
22472                 type : 'hidden',
22473                 cls : 'roo-hidden-value',
22474                 value : this.checked ? this.inputValue : this.valueOff
22475             };
22476         }
22477         
22478             
22479         if (this.weight) { // Validity check?
22480             cfg.cls += " " + this.inputType + "-" + this.weight;
22481         }
22482         
22483         if (this.disabled) {
22484             input.disabled=true;
22485         }
22486         
22487         if(this.checked){
22488             input.checked = this.checked;
22489         }
22490         
22491         if (this.name) {
22492             
22493             input.name = this.name;
22494             
22495             if(this.inputType != 'radio'){
22496                 hidden.name = this.name;
22497                 input.name = '_hidden_' + this.name;
22498             }
22499         }
22500         
22501         if (this.size) {
22502             input.cls += ' input-' + this.size;
22503         }
22504         
22505         var settings=this;
22506         
22507         ['xs','sm','md','lg'].map(function(size){
22508             if (settings[size]) {
22509                 cfg.cls += ' col-' + size + '-' + settings[size];
22510             }
22511         });
22512         
22513         var inputblock = input;
22514          
22515         if (this.before || this.after) {
22516             
22517             inputblock = {
22518                 cls : 'input-group',
22519                 cn :  [] 
22520             };
22521             
22522             if (this.before) {
22523                 inputblock.cn.push({
22524                     tag :'span',
22525                     cls : 'input-group-addon',
22526                     html : this.before
22527                 });
22528             }
22529             
22530             inputblock.cn.push(input);
22531             
22532             if(this.inputType != 'radio'){
22533                 inputblock.cn.push(hidden);
22534             }
22535             
22536             if (this.after) {
22537                 inputblock.cn.push({
22538                     tag :'span',
22539                     cls : 'input-group-addon',
22540                     html : this.after
22541                 });
22542             }
22543             
22544         }
22545         var boxLabelCfg = false;
22546         
22547         if(this.boxLabel){
22548            
22549             boxLabelCfg = {
22550                 tag: 'label',
22551                 //'for': id, // box label is handled by onclick - so no for...
22552                 cls: 'box-label',
22553                 html: this.boxLabel
22554             };
22555             if(this.tooltip){
22556                 boxLabelCfg.tooltip = this.tooltip;
22557             }
22558              
22559         }
22560         
22561         
22562         if (align ==='left' && this.fieldLabel.length) {
22563 //                Roo.log("left and has label");
22564             cfg.cn = [
22565                 {
22566                     tag: 'label',
22567                     'for' :  id,
22568                     cls : 'control-label',
22569                     html : this.fieldLabel
22570                 },
22571                 {
22572                     cls : "", 
22573                     cn: [
22574                         inputblock
22575                     ]
22576                 }
22577             ];
22578             
22579             if (boxLabelCfg) {
22580                 cfg.cn[1].cn.push(boxLabelCfg);
22581             }
22582             
22583             if(this.labelWidth > 12){
22584                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22585             }
22586             
22587             if(this.labelWidth < 13 && this.labelmd == 0){
22588                 this.labelmd = this.labelWidth;
22589             }
22590             
22591             if(this.labellg > 0){
22592                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22593                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22594             }
22595             
22596             if(this.labelmd > 0){
22597                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22598                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22599             }
22600             
22601             if(this.labelsm > 0){
22602                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22603                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22604             }
22605             
22606             if(this.labelxs > 0){
22607                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22608                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22609             }
22610             
22611         } else if ( this.fieldLabel.length) {
22612 //                Roo.log(" label");
22613                 cfg.cn = [
22614                    
22615                     {
22616                         tag: this.boxLabel ? 'span' : 'label',
22617                         'for': id,
22618                         cls: 'control-label box-input-label',
22619                         //cls : 'input-group-addon',
22620                         html : this.fieldLabel
22621                     },
22622                     
22623                     inputblock
22624                     
22625                 ];
22626                 if (boxLabelCfg) {
22627                     cfg.cn.push(boxLabelCfg);
22628                 }
22629
22630         } else {
22631             
22632 //                Roo.log(" no label && no align");
22633                 cfg.cn = [  inputblock ] ;
22634                 if (boxLabelCfg) {
22635                     cfg.cn.push(boxLabelCfg);
22636                 }
22637
22638                 
22639         }
22640         
22641        
22642         
22643         if(this.inputType != 'radio'){
22644             cfg.cn.push(hidden);
22645         }
22646         
22647         return cfg;
22648         
22649     },
22650     
22651     /**
22652      * return the real input element.
22653      */
22654     inputEl: function ()
22655     {
22656         return this.el.select('input.roo-' + this.inputType,true).first();
22657     },
22658     hiddenEl: function ()
22659     {
22660         return this.el.select('input.roo-hidden-value',true).first();
22661     },
22662     
22663     labelEl: function()
22664     {
22665         return this.el.select('label.control-label',true).first();
22666     },
22667     /* depricated... */
22668     
22669     label: function()
22670     {
22671         return this.labelEl();
22672     },
22673     
22674     boxLabelEl: function()
22675     {
22676         return this.el.select('label.box-label',true).first();
22677     },
22678     
22679     initEvents : function()
22680     {
22681 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22682         
22683         this.inputEl().on('click', this.onClick,  this);
22684         
22685         if (this.boxLabel) { 
22686             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
22687         }
22688         
22689         this.startValue = this.getValue();
22690         
22691         if(this.groupId){
22692             Roo.bootstrap.CheckBox.register(this);
22693         }
22694     },
22695     
22696     onClick : function(e)
22697     {   
22698         if(this.fireEvent('click', this, e) !== false){
22699             this.setChecked(!this.checked);
22700         }
22701         
22702     },
22703     
22704     setChecked : function(state,suppressEvent)
22705     {
22706         this.startValue = this.getValue();
22707
22708         if(this.inputType == 'radio'){
22709             
22710             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22711                 e.dom.checked = false;
22712             });
22713             
22714             this.inputEl().dom.checked = true;
22715             
22716             this.inputEl().dom.value = this.inputValue;
22717             
22718             if(suppressEvent !== true){
22719                 this.fireEvent('check', this, true);
22720             }
22721             
22722             this.validate();
22723             
22724             return;
22725         }
22726         
22727         this.checked = state;
22728         
22729         this.inputEl().dom.checked = state;
22730         
22731         
22732         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22733         
22734         if(suppressEvent !== true){
22735             this.fireEvent('check', this, state);
22736         }
22737         
22738         this.validate();
22739     },
22740     
22741     getValue : function()
22742     {
22743         if(this.inputType == 'radio'){
22744             return this.getGroupValue();
22745         }
22746         
22747         return this.hiddenEl().dom.value;
22748         
22749     },
22750     
22751     getGroupValue : function()
22752     {
22753         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22754             return '';
22755         }
22756         
22757         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22758     },
22759     
22760     setValue : function(v,suppressEvent)
22761     {
22762         if(this.inputType == 'radio'){
22763             this.setGroupValue(v, suppressEvent);
22764             return;
22765         }
22766         
22767         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22768         
22769         this.validate();
22770     },
22771     
22772     setGroupValue : function(v, suppressEvent)
22773     {
22774         this.startValue = this.getValue();
22775         
22776         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22777             e.dom.checked = false;
22778             
22779             if(e.dom.value == v){
22780                 e.dom.checked = true;
22781             }
22782         });
22783         
22784         if(suppressEvent !== true){
22785             this.fireEvent('check', this, true);
22786         }
22787
22788         this.validate();
22789         
22790         return;
22791     },
22792     
22793     validate : function()
22794     {
22795         if(this.getVisibilityEl().hasClass('hidden')){
22796             return true;
22797         }
22798         
22799         if(
22800                 this.disabled || 
22801                 (this.inputType == 'radio' && this.validateRadio()) ||
22802                 (this.inputType == 'checkbox' && this.validateCheckbox())
22803         ){
22804             this.markValid();
22805             return true;
22806         }
22807         
22808         this.markInvalid();
22809         return false;
22810     },
22811     
22812     validateRadio : function()
22813     {
22814         if(this.getVisibilityEl().hasClass('hidden')){
22815             return true;
22816         }
22817         
22818         if(this.allowBlank){
22819             return true;
22820         }
22821         
22822         var valid = false;
22823         
22824         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22825             if(!e.dom.checked){
22826                 return;
22827             }
22828             
22829             valid = true;
22830             
22831             return false;
22832         });
22833         
22834         return valid;
22835     },
22836     
22837     validateCheckbox : function()
22838     {
22839         if(!this.groupId){
22840             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22841             //return (this.getValue() == this.inputValue) ? true : false;
22842         }
22843         
22844         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22845         
22846         if(!group){
22847             return false;
22848         }
22849         
22850         var r = false;
22851         
22852         for(var i in group){
22853             if(group[i].el.isVisible(true)){
22854                 r = false;
22855                 break;
22856             }
22857             
22858             r = true;
22859         }
22860         
22861         for(var i in group){
22862             if(r){
22863                 break;
22864             }
22865             
22866             r = (group[i].getValue() == group[i].inputValue) ? true : false;
22867         }
22868         
22869         return r;
22870     },
22871     
22872     /**
22873      * Mark this field as valid
22874      */
22875     markValid : function()
22876     {
22877         var _this = this;
22878         
22879         this.fireEvent('valid', this);
22880         
22881         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22882         
22883         if(this.groupId){
22884             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22885         }
22886         
22887         if(label){
22888             label.markValid();
22889         }
22890
22891         if(this.inputType == 'radio'){
22892             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22893                 var fg = e.findParent('.form-group', false, true);
22894                 if (Roo.bootstrap.version == 3) {
22895                     fg.removeClass([_this.invalidClass, _this.validClass]);
22896                     fg.addClass(_this.validClass);
22897                 } else {
22898                     fg.removeClass(['is-valid', 'is-invalid']);
22899                     fg.addClass('is-valid');
22900                 }
22901             });
22902             
22903             return;
22904         }
22905
22906         if(!this.groupId){
22907             var fg = this.el.findParent('.form-group', false, true);
22908             if (Roo.bootstrap.version == 3) {
22909                 fg.removeClass([this.invalidClass, this.validClass]);
22910                 fg.addClass(this.validClass);
22911             } else {
22912                 fg.removeClass(['is-valid', 'is-invalid']);
22913                 fg.addClass('is-valid');
22914             }
22915             return;
22916         }
22917         
22918         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22919         
22920         if(!group){
22921             return;
22922         }
22923         
22924         for(var i in group){
22925             var fg = group[i].el.findParent('.form-group', false, true);
22926             if (Roo.bootstrap.version == 3) {
22927                 fg.removeClass([this.invalidClass, this.validClass]);
22928                 fg.addClass(this.validClass);
22929             } else {
22930                 fg.removeClass(['is-valid', 'is-invalid']);
22931                 fg.addClass('is-valid');
22932             }
22933         }
22934     },
22935     
22936      /**
22937      * Mark this field as invalid
22938      * @param {String} msg The validation message
22939      */
22940     markInvalid : function(msg)
22941     {
22942         if(this.allowBlank){
22943             return;
22944         }
22945         
22946         var _this = this;
22947         
22948         this.fireEvent('invalid', this, msg);
22949         
22950         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22951         
22952         if(this.groupId){
22953             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22954         }
22955         
22956         if(label){
22957             label.markInvalid();
22958         }
22959             
22960         if(this.inputType == 'radio'){
22961             
22962             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22963                 var fg = e.findParent('.form-group', false, true);
22964                 if (Roo.bootstrap.version == 3) {
22965                     fg.removeClass([_this.invalidClass, _this.validClass]);
22966                     fg.addClass(_this.invalidClass);
22967                 } else {
22968                     fg.removeClass(['is-invalid', 'is-valid']);
22969                     fg.addClass('is-invalid');
22970                 }
22971             });
22972             
22973             return;
22974         }
22975         
22976         if(!this.groupId){
22977             var fg = this.el.findParent('.form-group', false, true);
22978             if (Roo.bootstrap.version == 3) {
22979                 fg.removeClass([_this.invalidClass, _this.validClass]);
22980                 fg.addClass(_this.invalidClass);
22981             } else {
22982                 fg.removeClass(['is-invalid', 'is-valid']);
22983                 fg.addClass('is-invalid');
22984             }
22985             return;
22986         }
22987         
22988         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22989         
22990         if(!group){
22991             return;
22992         }
22993         
22994         for(var i in group){
22995             var fg = group[i].el.findParent('.form-group', false, true);
22996             if (Roo.bootstrap.version == 3) {
22997                 fg.removeClass([_this.invalidClass, _this.validClass]);
22998                 fg.addClass(_this.invalidClass);
22999             } else {
23000                 fg.removeClass(['is-invalid', 'is-valid']);
23001                 fg.addClass('is-invalid');
23002             }
23003         }
23004         
23005     },
23006     
23007     clearInvalid : function()
23008     {
23009         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23010         
23011         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23012         
23013         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23014         
23015         if (label && label.iconEl) {
23016             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23017             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23018         }
23019     },
23020     
23021     disable : function()
23022     {
23023         if(this.inputType != 'radio'){
23024             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23025             return;
23026         }
23027         
23028         var _this = this;
23029         
23030         if(this.rendered){
23031             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23032                 _this.getActionEl().addClass(this.disabledClass);
23033                 e.dom.disabled = true;
23034             });
23035         }
23036         
23037         this.disabled = true;
23038         this.fireEvent("disable", this);
23039         return this;
23040     },
23041
23042     enable : function()
23043     {
23044         if(this.inputType != 'radio'){
23045             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23046             return;
23047         }
23048         
23049         var _this = this;
23050         
23051         if(this.rendered){
23052             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23053                 _this.getActionEl().removeClass(this.disabledClass);
23054                 e.dom.disabled = false;
23055             });
23056         }
23057         
23058         this.disabled = false;
23059         this.fireEvent("enable", this);
23060         return this;
23061     },
23062     
23063     setBoxLabel : function(v)
23064     {
23065         this.boxLabel = v;
23066         
23067         if(this.rendered){
23068             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23069         }
23070     }
23071
23072 });
23073
23074 Roo.apply(Roo.bootstrap.CheckBox, {
23075     
23076     groups: {},
23077     
23078      /**
23079     * register a CheckBox Group
23080     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23081     */
23082     register : function(checkbox)
23083     {
23084         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23085             this.groups[checkbox.groupId] = {};
23086         }
23087         
23088         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23089             return;
23090         }
23091         
23092         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23093         
23094     },
23095     /**
23096     * fetch a CheckBox Group based on the group ID
23097     * @param {string} the group ID
23098     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23099     */
23100     get: function(groupId) {
23101         if (typeof(this.groups[groupId]) == 'undefined') {
23102             return false;
23103         }
23104         
23105         return this.groups[groupId] ;
23106     }
23107     
23108     
23109 });
23110 /*
23111  * - LGPL
23112  *
23113  * RadioItem
23114  * 
23115  */
23116
23117 /**
23118  * @class Roo.bootstrap.Radio
23119  * @extends Roo.bootstrap.Component
23120  * Bootstrap Radio class
23121  * @cfg {String} boxLabel - the label associated
23122  * @cfg {String} value - the value of radio
23123  * 
23124  * @constructor
23125  * Create a new Radio
23126  * @param {Object} config The config object
23127  */
23128 Roo.bootstrap.Radio = function(config){
23129     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23130     
23131 };
23132
23133 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23134     
23135     boxLabel : '',
23136     
23137     value : '',
23138     
23139     getAutoCreate : function()
23140     {
23141         var cfg = {
23142             tag : 'div',
23143             cls : 'form-group radio',
23144             cn : [
23145                 {
23146                     tag : 'label',
23147                     cls : 'box-label',
23148                     html : this.boxLabel
23149                 }
23150             ]
23151         };
23152         
23153         return cfg;
23154     },
23155     
23156     initEvents : function() 
23157     {
23158         this.parent().register(this);
23159         
23160         this.el.on('click', this.onClick, this);
23161         
23162     },
23163     
23164     onClick : function(e)
23165     {
23166         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23167             this.setChecked(true);
23168         }
23169     },
23170     
23171     setChecked : function(state, suppressEvent)
23172     {
23173         this.parent().setValue(this.value, suppressEvent);
23174         
23175     },
23176     
23177     setBoxLabel : function(v)
23178     {
23179         this.boxLabel = v;
23180         
23181         if(this.rendered){
23182             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23183         }
23184     }
23185     
23186 });
23187  
23188
23189  /*
23190  * - LGPL
23191  *
23192  * Input
23193  * 
23194  */
23195
23196 /**
23197  * @class Roo.bootstrap.SecurePass
23198  * @extends Roo.bootstrap.Input
23199  * Bootstrap SecurePass class
23200  *
23201  * 
23202  * @constructor
23203  * Create a new SecurePass
23204  * @param {Object} config The config object
23205  */
23206  
23207 Roo.bootstrap.SecurePass = function (config) {
23208     // these go here, so the translation tool can replace them..
23209     this.errors = {
23210         PwdEmpty: "Please type a password, and then retype it to confirm.",
23211         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23212         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23213         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23214         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23215         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23216         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23217         TooWeak: "Your password is Too Weak."
23218     },
23219     this.meterLabel = "Password strength:";
23220     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23221     this.meterClass = [
23222         "roo-password-meter-tooweak", 
23223         "roo-password-meter-weak", 
23224         "roo-password-meter-medium", 
23225         "roo-password-meter-strong", 
23226         "roo-password-meter-grey"
23227     ];
23228     
23229     this.errors = {};
23230     
23231     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23232 }
23233
23234 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23235     /**
23236      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23237      * {
23238      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23239      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23240      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23241      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23242      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23243      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23244      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23245      * })
23246      */
23247     // private
23248     
23249     meterWidth: 300,
23250     errorMsg :'',    
23251     errors: false,
23252     imageRoot: '/',
23253     /**
23254      * @cfg {String/Object} Label for the strength meter (defaults to
23255      * 'Password strength:')
23256      */
23257     // private
23258     meterLabel: '',
23259     /**
23260      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23261      * ['Weak', 'Medium', 'Strong'])
23262      */
23263     // private    
23264     pwdStrengths: false,    
23265     // private
23266     strength: 0,
23267     // private
23268     _lastPwd: null,
23269     // private
23270     kCapitalLetter: 0,
23271     kSmallLetter: 1,
23272     kDigit: 2,
23273     kPunctuation: 3,
23274     
23275     insecure: false,
23276     // private
23277     initEvents: function ()
23278     {
23279         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23280
23281         if (this.el.is('input[type=password]') && Roo.isSafari) {
23282             this.el.on('keydown', this.SafariOnKeyDown, this);
23283         }
23284
23285         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23286     },
23287     // private
23288     onRender: function (ct, position)
23289     {
23290         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23291         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23292         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23293
23294         this.trigger.createChild({
23295                    cn: [
23296                     {
23297                     //id: 'PwdMeter',
23298                     tag: 'div',
23299                     cls: 'roo-password-meter-grey col-xs-12',
23300                     style: {
23301                         //width: 0,
23302                         //width: this.meterWidth + 'px'                                                
23303                         }
23304                     },
23305                     {                            
23306                          cls: 'roo-password-meter-text'                          
23307                     }
23308                 ]            
23309         });
23310
23311          
23312         if (this.hideTrigger) {
23313             this.trigger.setDisplayed(false);
23314         }
23315         this.setSize(this.width || '', this.height || '');
23316     },
23317     // private
23318     onDestroy: function ()
23319     {
23320         if (this.trigger) {
23321             this.trigger.removeAllListeners();
23322             this.trigger.remove();
23323         }
23324         if (this.wrap) {
23325             this.wrap.remove();
23326         }
23327         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23328     },
23329     // private
23330     checkStrength: function ()
23331     {
23332         var pwd = this.inputEl().getValue();
23333         if (pwd == this._lastPwd) {
23334             return;
23335         }
23336
23337         var strength;
23338         if (this.ClientSideStrongPassword(pwd)) {
23339             strength = 3;
23340         } else if (this.ClientSideMediumPassword(pwd)) {
23341             strength = 2;
23342         } else if (this.ClientSideWeakPassword(pwd)) {
23343             strength = 1;
23344         } else {
23345             strength = 0;
23346         }
23347         
23348         Roo.log('strength1: ' + strength);
23349         
23350         //var pm = this.trigger.child('div/div/div').dom;
23351         var pm = this.trigger.child('div/div');
23352         pm.removeClass(this.meterClass);
23353         pm.addClass(this.meterClass[strength]);
23354                 
23355         
23356         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23357                 
23358         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23359         
23360         this._lastPwd = pwd;
23361     },
23362     reset: function ()
23363     {
23364         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23365         
23366         this._lastPwd = '';
23367         
23368         var pm = this.trigger.child('div/div');
23369         pm.removeClass(this.meterClass);
23370         pm.addClass('roo-password-meter-grey');        
23371         
23372         
23373         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23374         
23375         pt.innerHTML = '';
23376         this.inputEl().dom.type='password';
23377     },
23378     // private
23379     validateValue: function (value)
23380     {
23381         
23382         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23383             return false;
23384         }
23385         if (value.length == 0) {
23386             if (this.allowBlank) {
23387                 this.clearInvalid();
23388                 return true;
23389             }
23390
23391             this.markInvalid(this.errors.PwdEmpty);
23392             this.errorMsg = this.errors.PwdEmpty;
23393             return false;
23394         }
23395         
23396         if(this.insecure){
23397             return true;
23398         }
23399         
23400         if ('[\x21-\x7e]*'.match(value)) {
23401             this.markInvalid(this.errors.PwdBadChar);
23402             this.errorMsg = this.errors.PwdBadChar;
23403             return false;
23404         }
23405         if (value.length < 6) {
23406             this.markInvalid(this.errors.PwdShort);
23407             this.errorMsg = this.errors.PwdShort;
23408             return false;
23409         }
23410         if (value.length > 16) {
23411             this.markInvalid(this.errors.PwdLong);
23412             this.errorMsg = this.errors.PwdLong;
23413             return false;
23414         }
23415         var strength;
23416         if (this.ClientSideStrongPassword(value)) {
23417             strength = 3;
23418         } else if (this.ClientSideMediumPassword(value)) {
23419             strength = 2;
23420         } else if (this.ClientSideWeakPassword(value)) {
23421             strength = 1;
23422         } else {
23423             strength = 0;
23424         }
23425
23426         
23427         if (strength < 2) {
23428             //this.markInvalid(this.errors.TooWeak);
23429             this.errorMsg = this.errors.TooWeak;
23430             //return false;
23431         }
23432         
23433         
23434         console.log('strength2: ' + strength);
23435         
23436         //var pm = this.trigger.child('div/div/div').dom;
23437         
23438         var pm = this.trigger.child('div/div');
23439         pm.removeClass(this.meterClass);
23440         pm.addClass(this.meterClass[strength]);
23441                 
23442         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23443                 
23444         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23445         
23446         this.errorMsg = ''; 
23447         return true;
23448     },
23449     // private
23450     CharacterSetChecks: function (type)
23451     {
23452         this.type = type;
23453         this.fResult = false;
23454     },
23455     // private
23456     isctype: function (character, type)
23457     {
23458         switch (type) {  
23459             case this.kCapitalLetter:
23460                 if (character >= 'A' && character <= 'Z') {
23461                     return true;
23462                 }
23463                 break;
23464             
23465             case this.kSmallLetter:
23466                 if (character >= 'a' && character <= 'z') {
23467                     return true;
23468                 }
23469                 break;
23470             
23471             case this.kDigit:
23472                 if (character >= '0' && character <= '9') {
23473                     return true;
23474                 }
23475                 break;
23476             
23477             case this.kPunctuation:
23478                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23479                     return true;
23480                 }
23481                 break;
23482             
23483             default:
23484                 return false;
23485         }
23486
23487     },
23488     // private
23489     IsLongEnough: function (pwd, size)
23490     {
23491         return !(pwd == null || isNaN(size) || pwd.length < size);
23492     },
23493     // private
23494     SpansEnoughCharacterSets: function (word, nb)
23495     {
23496         if (!this.IsLongEnough(word, nb))
23497         {
23498             return false;
23499         }
23500
23501         var characterSetChecks = new Array(
23502             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23503             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23504         );
23505         
23506         for (var index = 0; index < word.length; ++index) {
23507             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23508                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23509                     characterSetChecks[nCharSet].fResult = true;
23510                     break;
23511                 }
23512             }
23513         }
23514
23515         var nCharSets = 0;
23516         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23517             if (characterSetChecks[nCharSet].fResult) {
23518                 ++nCharSets;
23519             }
23520         }
23521
23522         if (nCharSets < nb) {
23523             return false;
23524         }
23525         return true;
23526     },
23527     // private
23528     ClientSideStrongPassword: function (pwd)
23529     {
23530         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23531     },
23532     // private
23533     ClientSideMediumPassword: function (pwd)
23534     {
23535         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23536     },
23537     // private
23538     ClientSideWeakPassword: function (pwd)
23539     {
23540         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23541     }
23542           
23543 })//<script type="text/javascript">
23544
23545 /*
23546  * Based  Ext JS Library 1.1.1
23547  * Copyright(c) 2006-2007, Ext JS, LLC.
23548  * LGPL
23549  *
23550  */
23551  
23552 /**
23553  * @class Roo.HtmlEditorCore
23554  * @extends Roo.Component
23555  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23556  *
23557  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23558  */
23559
23560 Roo.HtmlEditorCore = function(config){
23561     
23562     
23563     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23564     
23565     
23566     this.addEvents({
23567         /**
23568          * @event initialize
23569          * Fires when the editor is fully initialized (including the iframe)
23570          * @param {Roo.HtmlEditorCore} this
23571          */
23572         initialize: true,
23573         /**
23574          * @event activate
23575          * Fires when the editor is first receives the focus. Any insertion must wait
23576          * until after this event.
23577          * @param {Roo.HtmlEditorCore} this
23578          */
23579         activate: true,
23580          /**
23581          * @event beforesync
23582          * Fires before the textarea is updated with content from the editor iframe. Return false
23583          * to cancel the sync.
23584          * @param {Roo.HtmlEditorCore} this
23585          * @param {String} html
23586          */
23587         beforesync: true,
23588          /**
23589          * @event beforepush
23590          * Fires before the iframe editor is updated with content from the textarea. Return false
23591          * to cancel the push.
23592          * @param {Roo.HtmlEditorCore} this
23593          * @param {String} html
23594          */
23595         beforepush: true,
23596          /**
23597          * @event sync
23598          * Fires when the textarea is updated with content from the editor iframe.
23599          * @param {Roo.HtmlEditorCore} this
23600          * @param {String} html
23601          */
23602         sync: true,
23603          /**
23604          * @event push
23605          * Fires when the iframe editor is updated with content from the textarea.
23606          * @param {Roo.HtmlEditorCore} this
23607          * @param {String} html
23608          */
23609         push: true,
23610         
23611         /**
23612          * @event editorevent
23613          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23614          * @param {Roo.HtmlEditorCore} this
23615          */
23616         editorevent: true
23617         
23618     });
23619     
23620     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23621     
23622     // defaults : white / black...
23623     this.applyBlacklists();
23624     
23625     
23626     
23627 };
23628
23629
23630 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
23631
23632
23633      /**
23634      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
23635      */
23636     
23637     owner : false,
23638     
23639      /**
23640      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23641      *                        Roo.resizable.
23642      */
23643     resizable : false,
23644      /**
23645      * @cfg {Number} height (in pixels)
23646      */   
23647     height: 300,
23648    /**
23649      * @cfg {Number} width (in pixels)
23650      */   
23651     width: 500,
23652     
23653     /**
23654      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23655      * 
23656      */
23657     stylesheets: false,
23658     
23659     // id of frame..
23660     frameId: false,
23661     
23662     // private properties
23663     validationEvent : false,
23664     deferHeight: true,
23665     initialized : false,
23666     activated : false,
23667     sourceEditMode : false,
23668     onFocus : Roo.emptyFn,
23669     iframePad:3,
23670     hideMode:'offsets',
23671     
23672     clearUp: true,
23673     
23674     // blacklist + whitelisted elements..
23675     black: false,
23676     white: false,
23677      
23678     bodyCls : '',
23679
23680     /**
23681      * Protected method that will not generally be called directly. It
23682      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23683      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23684      */
23685     getDocMarkup : function(){
23686         // body styles..
23687         var st = '';
23688         
23689         // inherit styels from page...?? 
23690         if (this.stylesheets === false) {
23691             
23692             Roo.get(document.head).select('style').each(function(node) {
23693                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23694             });
23695             
23696             Roo.get(document.head).select('link').each(function(node) { 
23697                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23698             });
23699             
23700         } else if (!this.stylesheets.length) {
23701                 // simple..
23702                 st = '<style type="text/css">' +
23703                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23704                    '</style>';
23705         } else { 
23706             st = '<style type="text/css">' +
23707                     this.stylesheets +
23708                 '</style>';
23709         }
23710         
23711         st +=  '<style type="text/css">' +
23712             'IMG { cursor: pointer } ' +
23713         '</style>';
23714
23715         var cls = 'roo-htmleditor-body';
23716         
23717         if(this.bodyCls.length){
23718             cls += ' ' + this.bodyCls;
23719         }
23720         
23721         return '<html><head>' + st  +
23722             //<style type="text/css">' +
23723             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23724             //'</style>' +
23725             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
23726     },
23727
23728     // private
23729     onRender : function(ct, position)
23730     {
23731         var _t = this;
23732         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23733         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23734         
23735         
23736         this.el.dom.style.border = '0 none';
23737         this.el.dom.setAttribute('tabIndex', -1);
23738         this.el.addClass('x-hidden hide');
23739         
23740         
23741         
23742         if(Roo.isIE){ // fix IE 1px bogus margin
23743             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23744         }
23745        
23746         
23747         this.frameId = Roo.id();
23748         
23749          
23750         
23751         var iframe = this.owner.wrap.createChild({
23752             tag: 'iframe',
23753             cls: 'form-control', // bootstrap..
23754             id: this.frameId,
23755             name: this.frameId,
23756             frameBorder : 'no',
23757             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
23758         }, this.el
23759         );
23760         
23761         
23762         this.iframe = iframe.dom;
23763
23764          this.assignDocWin();
23765         
23766         this.doc.designMode = 'on';
23767        
23768         this.doc.open();
23769         this.doc.write(this.getDocMarkup());
23770         this.doc.close();
23771
23772         
23773         var task = { // must defer to wait for browser to be ready
23774             run : function(){
23775                 //console.log("run task?" + this.doc.readyState);
23776                 this.assignDocWin();
23777                 if(this.doc.body || this.doc.readyState == 'complete'){
23778                     try {
23779                         this.doc.designMode="on";
23780                     } catch (e) {
23781                         return;
23782                     }
23783                     Roo.TaskMgr.stop(task);
23784                     this.initEditor.defer(10, this);
23785                 }
23786             },
23787             interval : 10,
23788             duration: 10000,
23789             scope: this
23790         };
23791         Roo.TaskMgr.start(task);
23792
23793     },
23794
23795     // private
23796     onResize : function(w, h)
23797     {
23798          Roo.log('resize: ' +w + ',' + h );
23799         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23800         if(!this.iframe){
23801             return;
23802         }
23803         if(typeof w == 'number'){
23804             
23805             this.iframe.style.width = w + 'px';
23806         }
23807         if(typeof h == 'number'){
23808             
23809             this.iframe.style.height = h + 'px';
23810             if(this.doc){
23811                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23812             }
23813         }
23814         
23815     },
23816
23817     /**
23818      * Toggles the editor between standard and source edit mode.
23819      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23820      */
23821     toggleSourceEdit : function(sourceEditMode){
23822         
23823         this.sourceEditMode = sourceEditMode === true;
23824         
23825         if(this.sourceEditMode){
23826  
23827             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
23828             
23829         }else{
23830             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23831             //this.iframe.className = '';
23832             this.deferFocus();
23833         }
23834         //this.setSize(this.owner.wrap.getSize());
23835         //this.fireEvent('editmodechange', this, this.sourceEditMode);
23836     },
23837
23838     
23839   
23840
23841     /**
23842      * Protected method that will not generally be called directly. If you need/want
23843      * custom HTML cleanup, this is the method you should override.
23844      * @param {String} html The HTML to be cleaned
23845      * return {String} The cleaned HTML
23846      */
23847     cleanHtml : function(html){
23848         html = String(html);
23849         if(html.length > 5){
23850             if(Roo.isSafari){ // strip safari nonsense
23851                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23852             }
23853         }
23854         if(html == '&nbsp;'){
23855             html = '';
23856         }
23857         return html;
23858     },
23859
23860     /**
23861      * HTML Editor -> Textarea
23862      * Protected method that will not generally be called directly. Syncs the contents
23863      * of the editor iframe with the textarea.
23864      */
23865     syncValue : function(){
23866         if(this.initialized){
23867             var bd = (this.doc.body || this.doc.documentElement);
23868             //this.cleanUpPaste(); -- this is done else where and causes havoc..
23869             var html = bd.innerHTML;
23870             if(Roo.isSafari){
23871                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23872                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23873                 if(m && m[1]){
23874                     html = '<div style="'+m[0]+'">' + html + '</div>';
23875                 }
23876             }
23877             html = this.cleanHtml(html);
23878             // fix up the special chars.. normaly like back quotes in word...
23879             // however we do not want to do this with chinese..
23880             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23881                 
23882                 var cc = match.charCodeAt();
23883
23884                 // Get the character value, handling surrogate pairs
23885                 if (match.length == 2) {
23886                     // It's a surrogate pair, calculate the Unicode code point
23887                     var high = match.charCodeAt(0) - 0xD800;
23888                     var low  = match.charCodeAt(1) - 0xDC00;
23889                     cc = (high * 0x400) + low + 0x10000;
23890                 }  else if (
23891                     (cc >= 0x4E00 && cc < 0xA000 ) ||
23892                     (cc >= 0x3400 && cc < 0x4E00 ) ||
23893                     (cc >= 0xf900 && cc < 0xfb00 )
23894                 ) {
23895                         return match;
23896                 }  
23897          
23898                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
23899                 return "&#" + cc + ";";
23900                 
23901                 
23902             });
23903             
23904             
23905              
23906             if(this.owner.fireEvent('beforesync', this, html) !== false){
23907                 this.el.dom.value = html;
23908                 this.owner.fireEvent('sync', this, html);
23909             }
23910         }
23911     },
23912
23913     /**
23914      * Protected method that will not generally be called directly. Pushes the value of the textarea
23915      * into the iframe editor.
23916      */
23917     pushValue : function(){
23918         if(this.initialized){
23919             var v = this.el.dom.value.trim();
23920             
23921 //            if(v.length < 1){
23922 //                v = '&#160;';
23923 //            }
23924             
23925             if(this.owner.fireEvent('beforepush', this, v) !== false){
23926                 var d = (this.doc.body || this.doc.documentElement);
23927                 d.innerHTML = v;
23928                 this.cleanUpPaste();
23929                 this.el.dom.value = d.innerHTML;
23930                 this.owner.fireEvent('push', this, v);
23931             }
23932         }
23933     },
23934
23935     // private
23936     deferFocus : function(){
23937         this.focus.defer(10, this);
23938     },
23939
23940     // doc'ed in Field
23941     focus : function(){
23942         if(this.win && !this.sourceEditMode){
23943             this.win.focus();
23944         }else{
23945             this.el.focus();
23946         }
23947     },
23948     
23949     assignDocWin: function()
23950     {
23951         var iframe = this.iframe;
23952         
23953          if(Roo.isIE){
23954             this.doc = iframe.contentWindow.document;
23955             this.win = iframe.contentWindow;
23956         } else {
23957 //            if (!Roo.get(this.frameId)) {
23958 //                return;
23959 //            }
23960 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23961 //            this.win = Roo.get(this.frameId).dom.contentWindow;
23962             
23963             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
23964                 return;
23965             }
23966             
23967             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23968             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
23969         }
23970     },
23971     
23972     // private
23973     initEditor : function(){
23974         //console.log("INIT EDITOR");
23975         this.assignDocWin();
23976         
23977         
23978         
23979         this.doc.designMode="on";
23980         this.doc.open();
23981         this.doc.write(this.getDocMarkup());
23982         this.doc.close();
23983         
23984         var dbody = (this.doc.body || this.doc.documentElement);
23985         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
23986         // this copies styles from the containing element into thsi one..
23987         // not sure why we need all of this..
23988         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
23989         
23990         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
23991         //ss['background-attachment'] = 'fixed'; // w3c
23992         dbody.bgProperties = 'fixed'; // ie
23993         //Roo.DomHelper.applyStyles(dbody, ss);
23994         Roo.EventManager.on(this.doc, {
23995             //'mousedown': this.onEditorEvent,
23996             'mouseup': this.onEditorEvent,
23997             'dblclick': this.onEditorEvent,
23998             'click': this.onEditorEvent,
23999             'keyup': this.onEditorEvent,
24000             buffer:100,
24001             scope: this
24002         });
24003         if(Roo.isGecko){
24004             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24005         }
24006         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24007             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24008         }
24009         this.initialized = true;
24010
24011         this.owner.fireEvent('initialize', this);
24012         this.pushValue();
24013     },
24014
24015     // private
24016     onDestroy : function(){
24017         
24018         
24019         
24020         if(this.rendered){
24021             
24022             //for (var i =0; i < this.toolbars.length;i++) {
24023             //    // fixme - ask toolbars for heights?
24024             //    this.toolbars[i].onDestroy();
24025            // }
24026             
24027             //this.wrap.dom.innerHTML = '';
24028             //this.wrap.remove();
24029         }
24030     },
24031
24032     // private
24033     onFirstFocus : function(){
24034         
24035         this.assignDocWin();
24036         
24037         
24038         this.activated = true;
24039          
24040     
24041         if(Roo.isGecko){ // prevent silly gecko errors
24042             this.win.focus();
24043             var s = this.win.getSelection();
24044             if(!s.focusNode || s.focusNode.nodeType != 3){
24045                 var r = s.getRangeAt(0);
24046                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24047                 r.collapse(true);
24048                 this.deferFocus();
24049             }
24050             try{
24051                 this.execCmd('useCSS', true);
24052                 this.execCmd('styleWithCSS', false);
24053             }catch(e){}
24054         }
24055         this.owner.fireEvent('activate', this);
24056     },
24057
24058     // private
24059     adjustFont: function(btn){
24060         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24061         //if(Roo.isSafari){ // safari
24062         //    adjust *= 2;
24063        // }
24064         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24065         if(Roo.isSafari){ // safari
24066             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24067             v =  (v < 10) ? 10 : v;
24068             v =  (v > 48) ? 48 : v;
24069             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24070             
24071         }
24072         
24073         
24074         v = Math.max(1, v+adjust);
24075         
24076         this.execCmd('FontSize', v  );
24077     },
24078
24079     onEditorEvent : function(e)
24080     {
24081         this.owner.fireEvent('editorevent', this, e);
24082       //  this.updateToolbar();
24083         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24084     },
24085
24086     insertTag : function(tg)
24087     {
24088         // could be a bit smarter... -> wrap the current selected tRoo..
24089         if (tg.toLowerCase() == 'span' ||
24090             tg.toLowerCase() == 'code' ||
24091             tg.toLowerCase() == 'sup' ||
24092             tg.toLowerCase() == 'sub' 
24093             ) {
24094             
24095             range = this.createRange(this.getSelection());
24096             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24097             wrappingNode.appendChild(range.extractContents());
24098             range.insertNode(wrappingNode);
24099
24100             return;
24101             
24102             
24103             
24104         }
24105         this.execCmd("formatblock",   tg);
24106         
24107     },
24108     
24109     insertText : function(txt)
24110     {
24111         
24112         
24113         var range = this.createRange();
24114         range.deleteContents();
24115                //alert(Sender.getAttribute('label'));
24116                
24117         range.insertNode(this.doc.createTextNode(txt));
24118     } ,
24119     
24120      
24121
24122     /**
24123      * Executes a Midas editor command on the editor document and performs necessary focus and
24124      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24125      * @param {String} cmd The Midas command
24126      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24127      */
24128     relayCmd : function(cmd, value){
24129         this.win.focus();
24130         this.execCmd(cmd, value);
24131         this.owner.fireEvent('editorevent', this);
24132         //this.updateToolbar();
24133         this.owner.deferFocus();
24134     },
24135
24136     /**
24137      * Executes a Midas editor command directly on the editor document.
24138      * For visual commands, you should use {@link #relayCmd} instead.
24139      * <b>This should only be called after the editor is initialized.</b>
24140      * @param {String} cmd The Midas command
24141      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24142      */
24143     execCmd : function(cmd, value){
24144         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24145         this.syncValue();
24146     },
24147  
24148  
24149    
24150     /**
24151      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24152      * to insert tRoo.
24153      * @param {String} text | dom node.. 
24154      */
24155     insertAtCursor : function(text)
24156     {
24157         
24158         if(!this.activated){
24159             return;
24160         }
24161         /*
24162         if(Roo.isIE){
24163             this.win.focus();
24164             var r = this.doc.selection.createRange();
24165             if(r){
24166                 r.collapse(true);
24167                 r.pasteHTML(text);
24168                 this.syncValue();
24169                 this.deferFocus();
24170             
24171             }
24172             return;
24173         }
24174         */
24175         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24176             this.win.focus();
24177             
24178             
24179             // from jquery ui (MIT licenced)
24180             var range, node;
24181             var win = this.win;
24182             
24183             if (win.getSelection && win.getSelection().getRangeAt) {
24184                 range = win.getSelection().getRangeAt(0);
24185                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24186                 range.insertNode(node);
24187             } else if (win.document.selection && win.document.selection.createRange) {
24188                 // no firefox support
24189                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24190                 win.document.selection.createRange().pasteHTML(txt);
24191             } else {
24192                 // no firefox support
24193                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24194                 this.execCmd('InsertHTML', txt);
24195             } 
24196             
24197             this.syncValue();
24198             
24199             this.deferFocus();
24200         }
24201     },
24202  // private
24203     mozKeyPress : function(e){
24204         if(e.ctrlKey){
24205             var c = e.getCharCode(), cmd;
24206           
24207             if(c > 0){
24208                 c = String.fromCharCode(c).toLowerCase();
24209                 switch(c){
24210                     case 'b':
24211                         cmd = 'bold';
24212                         break;
24213                     case 'i':
24214                         cmd = 'italic';
24215                         break;
24216                     
24217                     case 'u':
24218                         cmd = 'underline';
24219                         break;
24220                     
24221                     case 'v':
24222                         this.cleanUpPaste.defer(100, this);
24223                         return;
24224                         
24225                 }
24226                 if(cmd){
24227                     this.win.focus();
24228                     this.execCmd(cmd);
24229                     this.deferFocus();
24230                     e.preventDefault();
24231                 }
24232                 
24233             }
24234         }
24235     },
24236
24237     // private
24238     fixKeys : function(){ // load time branching for fastest keydown performance
24239         if(Roo.isIE){
24240             return function(e){
24241                 var k = e.getKey(), r;
24242                 if(k == e.TAB){
24243                     e.stopEvent();
24244                     r = this.doc.selection.createRange();
24245                     if(r){
24246                         r.collapse(true);
24247                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24248                         this.deferFocus();
24249                     }
24250                     return;
24251                 }
24252                 
24253                 if(k == e.ENTER){
24254                     r = this.doc.selection.createRange();
24255                     if(r){
24256                         var target = r.parentElement();
24257                         if(!target || target.tagName.toLowerCase() != 'li'){
24258                             e.stopEvent();
24259                             r.pasteHTML('<br />');
24260                             r.collapse(false);
24261                             r.select();
24262                         }
24263                     }
24264                 }
24265                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24266                     this.cleanUpPaste.defer(100, this);
24267                     return;
24268                 }
24269                 
24270                 
24271             };
24272         }else if(Roo.isOpera){
24273             return function(e){
24274                 var k = e.getKey();
24275                 if(k == e.TAB){
24276                     e.stopEvent();
24277                     this.win.focus();
24278                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24279                     this.deferFocus();
24280                 }
24281                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24282                     this.cleanUpPaste.defer(100, this);
24283                     return;
24284                 }
24285                 
24286             };
24287         }else if(Roo.isSafari){
24288             return function(e){
24289                 var k = e.getKey();
24290                 
24291                 if(k == e.TAB){
24292                     e.stopEvent();
24293                     this.execCmd('InsertText','\t');
24294                     this.deferFocus();
24295                     return;
24296                 }
24297                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24298                     this.cleanUpPaste.defer(100, this);
24299                     return;
24300                 }
24301                 
24302              };
24303         }
24304     }(),
24305     
24306     getAllAncestors: function()
24307     {
24308         var p = this.getSelectedNode();
24309         var a = [];
24310         if (!p) {
24311             a.push(p); // push blank onto stack..
24312             p = this.getParentElement();
24313         }
24314         
24315         
24316         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24317             a.push(p);
24318             p = p.parentNode;
24319         }
24320         a.push(this.doc.body);
24321         return a;
24322     },
24323     lastSel : false,
24324     lastSelNode : false,
24325     
24326     
24327     getSelection : function() 
24328     {
24329         this.assignDocWin();
24330         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24331     },
24332     
24333     getSelectedNode: function() 
24334     {
24335         // this may only work on Gecko!!!
24336         
24337         // should we cache this!!!!
24338         
24339         
24340         
24341          
24342         var range = this.createRange(this.getSelection()).cloneRange();
24343         
24344         if (Roo.isIE) {
24345             var parent = range.parentElement();
24346             while (true) {
24347                 var testRange = range.duplicate();
24348                 testRange.moveToElementText(parent);
24349                 if (testRange.inRange(range)) {
24350                     break;
24351                 }
24352                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24353                     break;
24354                 }
24355                 parent = parent.parentElement;
24356             }
24357             return parent;
24358         }
24359         
24360         // is ancestor a text element.
24361         var ac =  range.commonAncestorContainer;
24362         if (ac.nodeType == 3) {
24363             ac = ac.parentNode;
24364         }
24365         
24366         var ar = ac.childNodes;
24367          
24368         var nodes = [];
24369         var other_nodes = [];
24370         var has_other_nodes = false;
24371         for (var i=0;i<ar.length;i++) {
24372             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24373                 continue;
24374             }
24375             // fullly contained node.
24376             
24377             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24378                 nodes.push(ar[i]);
24379                 continue;
24380             }
24381             
24382             // probably selected..
24383             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24384                 other_nodes.push(ar[i]);
24385                 continue;
24386             }
24387             // outer..
24388             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24389                 continue;
24390             }
24391             
24392             
24393             has_other_nodes = true;
24394         }
24395         if (!nodes.length && other_nodes.length) {
24396             nodes= other_nodes;
24397         }
24398         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24399             return false;
24400         }
24401         
24402         return nodes[0];
24403     },
24404     createRange: function(sel)
24405     {
24406         // this has strange effects when using with 
24407         // top toolbar - not sure if it's a great idea.
24408         //this.editor.contentWindow.focus();
24409         if (typeof sel != "undefined") {
24410             try {
24411                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24412             } catch(e) {
24413                 return this.doc.createRange();
24414             }
24415         } else {
24416             return this.doc.createRange();
24417         }
24418     },
24419     getParentElement: function()
24420     {
24421         
24422         this.assignDocWin();
24423         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24424         
24425         var range = this.createRange(sel);
24426          
24427         try {
24428             var p = range.commonAncestorContainer;
24429             while (p.nodeType == 3) { // text node
24430                 p = p.parentNode;
24431             }
24432             return p;
24433         } catch (e) {
24434             return null;
24435         }
24436     
24437     },
24438     /***
24439      *
24440      * Range intersection.. the hard stuff...
24441      *  '-1' = before
24442      *  '0' = hits..
24443      *  '1' = after.
24444      *         [ -- selected range --- ]
24445      *   [fail]                        [fail]
24446      *
24447      *    basically..
24448      *      if end is before start or  hits it. fail.
24449      *      if start is after end or hits it fail.
24450      *
24451      *   if either hits (but other is outside. - then it's not 
24452      *   
24453      *    
24454      **/
24455     
24456     
24457     // @see http://www.thismuchiknow.co.uk/?p=64.
24458     rangeIntersectsNode : function(range, node)
24459     {
24460         var nodeRange = node.ownerDocument.createRange();
24461         try {
24462             nodeRange.selectNode(node);
24463         } catch (e) {
24464             nodeRange.selectNodeContents(node);
24465         }
24466     
24467         var rangeStartRange = range.cloneRange();
24468         rangeStartRange.collapse(true);
24469     
24470         var rangeEndRange = range.cloneRange();
24471         rangeEndRange.collapse(false);
24472     
24473         var nodeStartRange = nodeRange.cloneRange();
24474         nodeStartRange.collapse(true);
24475     
24476         var nodeEndRange = nodeRange.cloneRange();
24477         nodeEndRange.collapse(false);
24478     
24479         return rangeStartRange.compareBoundaryPoints(
24480                  Range.START_TO_START, nodeEndRange) == -1 &&
24481                rangeEndRange.compareBoundaryPoints(
24482                  Range.START_TO_START, nodeStartRange) == 1;
24483         
24484          
24485     },
24486     rangeCompareNode : function(range, node)
24487     {
24488         var nodeRange = node.ownerDocument.createRange();
24489         try {
24490             nodeRange.selectNode(node);
24491         } catch (e) {
24492             nodeRange.selectNodeContents(node);
24493         }
24494         
24495         
24496         range.collapse(true);
24497     
24498         nodeRange.collapse(true);
24499      
24500         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24501         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24502          
24503         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24504         
24505         var nodeIsBefore   =  ss == 1;
24506         var nodeIsAfter    = ee == -1;
24507         
24508         if (nodeIsBefore && nodeIsAfter) {
24509             return 0; // outer
24510         }
24511         if (!nodeIsBefore && nodeIsAfter) {
24512             return 1; //right trailed.
24513         }
24514         
24515         if (nodeIsBefore && !nodeIsAfter) {
24516             return 2;  // left trailed.
24517         }
24518         // fully contined.
24519         return 3;
24520     },
24521
24522     // private? - in a new class?
24523     cleanUpPaste :  function()
24524     {
24525         // cleans up the whole document..
24526         Roo.log('cleanuppaste');
24527         
24528         this.cleanUpChildren(this.doc.body);
24529         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24530         if (clean != this.doc.body.innerHTML) {
24531             this.doc.body.innerHTML = clean;
24532         }
24533         
24534     },
24535     
24536     cleanWordChars : function(input) {// change the chars to hex code
24537         var he = Roo.HtmlEditorCore;
24538         
24539         var output = input;
24540         Roo.each(he.swapCodes, function(sw) { 
24541             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24542             
24543             output = output.replace(swapper, sw[1]);
24544         });
24545         
24546         return output;
24547     },
24548     
24549     
24550     cleanUpChildren : function (n)
24551     {
24552         if (!n.childNodes.length) {
24553             return;
24554         }
24555         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24556            this.cleanUpChild(n.childNodes[i]);
24557         }
24558     },
24559     
24560     
24561         
24562     
24563     cleanUpChild : function (node)
24564     {
24565         var ed = this;
24566         //console.log(node);
24567         if (node.nodeName == "#text") {
24568             // clean up silly Windows -- stuff?
24569             return; 
24570         }
24571         if (node.nodeName == "#comment") {
24572             node.parentNode.removeChild(node);
24573             // clean up silly Windows -- stuff?
24574             return; 
24575         }
24576         var lcname = node.tagName.toLowerCase();
24577         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24578         // whitelist of tags..
24579         
24580         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24581             // remove node.
24582             node.parentNode.removeChild(node);
24583             return;
24584             
24585         }
24586         
24587         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24588         
24589         // spans with no attributes - just remove them..
24590         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24591             remove_keep_children = true;
24592         }
24593         
24594         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24595         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24596         
24597         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24598         //    remove_keep_children = true;
24599         //}
24600         
24601         if (remove_keep_children) {
24602             this.cleanUpChildren(node);
24603             // inserts everything just before this node...
24604             while (node.childNodes.length) {
24605                 var cn = node.childNodes[0];
24606                 node.removeChild(cn);
24607                 node.parentNode.insertBefore(cn, node);
24608             }
24609             node.parentNode.removeChild(node);
24610             return;
24611         }
24612         
24613         if (!node.attributes || !node.attributes.length) {
24614             
24615           
24616             
24617             
24618             this.cleanUpChildren(node);
24619             return;
24620         }
24621         
24622         function cleanAttr(n,v)
24623         {
24624             
24625             if (v.match(/^\./) || v.match(/^\//)) {
24626                 return;
24627             }
24628             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24629                 return;
24630             }
24631             if (v.match(/^#/)) {
24632                 return;
24633             }
24634 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24635             node.removeAttribute(n);
24636             
24637         }
24638         
24639         var cwhite = this.cwhite;
24640         var cblack = this.cblack;
24641             
24642         function cleanStyle(n,v)
24643         {
24644             if (v.match(/expression/)) { //XSS?? should we even bother..
24645                 node.removeAttribute(n);
24646                 return;
24647             }
24648             
24649             var parts = v.split(/;/);
24650             var clean = [];
24651             
24652             Roo.each(parts, function(p) {
24653                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24654                 if (!p.length) {
24655                     return true;
24656                 }
24657                 var l = p.split(':').shift().replace(/\s+/g,'');
24658                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24659                 
24660                 if ( cwhite.length && cblack.indexOf(l) > -1) {
24661 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24662                     //node.removeAttribute(n);
24663                     return true;
24664                 }
24665                 //Roo.log()
24666                 // only allow 'c whitelisted system attributes'
24667                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
24668 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24669                     //node.removeAttribute(n);
24670                     return true;
24671                 }
24672                 
24673                 
24674                  
24675                 
24676                 clean.push(p);
24677                 return true;
24678             });
24679             if (clean.length) { 
24680                 node.setAttribute(n, clean.join(';'));
24681             } else {
24682                 node.removeAttribute(n);
24683             }
24684             
24685         }
24686         
24687         
24688         for (var i = node.attributes.length-1; i > -1 ; i--) {
24689             var a = node.attributes[i];
24690             //console.log(a);
24691             
24692             if (a.name.toLowerCase().substr(0,2)=='on')  {
24693                 node.removeAttribute(a.name);
24694                 continue;
24695             }
24696             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24697                 node.removeAttribute(a.name);
24698                 continue;
24699             }
24700             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24701                 cleanAttr(a.name,a.value); // fixme..
24702                 continue;
24703             }
24704             if (a.name == 'style') {
24705                 cleanStyle(a.name,a.value);
24706                 continue;
24707             }
24708             /// clean up MS crap..
24709             // tecnically this should be a list of valid class'es..
24710             
24711             
24712             if (a.name == 'class') {
24713                 if (a.value.match(/^Mso/)) {
24714                     node.removeAttribute('class');
24715                 }
24716                 
24717                 if (a.value.match(/^body$/)) {
24718                     node.removeAttribute('class');
24719                 }
24720                 continue;
24721             }
24722             
24723             // style cleanup!?
24724             // class cleanup?
24725             
24726         }
24727         
24728         
24729         this.cleanUpChildren(node);
24730         
24731         
24732     },
24733     
24734     /**
24735      * Clean up MS wordisms...
24736      */
24737     cleanWord : function(node)
24738     {
24739         if (!node) {
24740             this.cleanWord(this.doc.body);
24741             return;
24742         }
24743         
24744         if(
24745                 node.nodeName == 'SPAN' &&
24746                 !node.hasAttributes() &&
24747                 node.childNodes.length == 1 &&
24748                 node.firstChild.nodeName == "#text"  
24749         ) {
24750             var textNode = node.firstChild;
24751             node.removeChild(textNode);
24752             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24753                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
24754             }
24755             node.parentNode.insertBefore(textNode, node);
24756             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24757                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24758             }
24759             node.parentNode.removeChild(node);
24760         }
24761         
24762         if (node.nodeName == "#text") {
24763             // clean up silly Windows -- stuff?
24764             return; 
24765         }
24766         if (node.nodeName == "#comment") {
24767             node.parentNode.removeChild(node);
24768             // clean up silly Windows -- stuff?
24769             return; 
24770         }
24771         
24772         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24773             node.parentNode.removeChild(node);
24774             return;
24775         }
24776         //Roo.log(node.tagName);
24777         // remove - but keep children..
24778         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24779             //Roo.log('-- removed');
24780             while (node.childNodes.length) {
24781                 var cn = node.childNodes[0];
24782                 node.removeChild(cn);
24783                 node.parentNode.insertBefore(cn, node);
24784                 // move node to parent - and clean it..
24785                 this.cleanWord(cn);
24786             }
24787             node.parentNode.removeChild(node);
24788             /// no need to iterate chidlren = it's got none..
24789             //this.iterateChildren(node, this.cleanWord);
24790             return;
24791         }
24792         // clean styles
24793         if (node.className.length) {
24794             
24795             var cn = node.className.split(/\W+/);
24796             var cna = [];
24797             Roo.each(cn, function(cls) {
24798                 if (cls.match(/Mso[a-zA-Z]+/)) {
24799                     return;
24800                 }
24801                 cna.push(cls);
24802             });
24803             node.className = cna.length ? cna.join(' ') : '';
24804             if (!cna.length) {
24805                 node.removeAttribute("class");
24806             }
24807         }
24808         
24809         if (node.hasAttribute("lang")) {
24810             node.removeAttribute("lang");
24811         }
24812         
24813         if (node.hasAttribute("style")) {
24814             
24815             var styles = node.getAttribute("style").split(";");
24816             var nstyle = [];
24817             Roo.each(styles, function(s) {
24818                 if (!s.match(/:/)) {
24819                     return;
24820                 }
24821                 var kv = s.split(":");
24822                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24823                     return;
24824                 }
24825                 // what ever is left... we allow.
24826                 nstyle.push(s);
24827             });
24828             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24829             if (!nstyle.length) {
24830                 node.removeAttribute('style');
24831             }
24832         }
24833         this.iterateChildren(node, this.cleanWord);
24834         
24835         
24836         
24837     },
24838     /**
24839      * iterateChildren of a Node, calling fn each time, using this as the scole..
24840      * @param {DomNode} node node to iterate children of.
24841      * @param {Function} fn method of this class to call on each item.
24842      */
24843     iterateChildren : function(node, fn)
24844     {
24845         if (!node.childNodes.length) {
24846                 return;
24847         }
24848         for (var i = node.childNodes.length-1; i > -1 ; i--) {
24849            fn.call(this, node.childNodes[i])
24850         }
24851     },
24852     
24853     
24854     /**
24855      * cleanTableWidths.
24856      *
24857      * Quite often pasting from word etc.. results in tables with column and widths.
24858      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24859      *
24860      */
24861     cleanTableWidths : function(node)
24862     {
24863          
24864          
24865         if (!node) {
24866             this.cleanTableWidths(this.doc.body);
24867             return;
24868         }
24869         
24870         // ignore list...
24871         if (node.nodeName == "#text" || node.nodeName == "#comment") {
24872             return; 
24873         }
24874         Roo.log(node.tagName);
24875         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24876             this.iterateChildren(node, this.cleanTableWidths);
24877             return;
24878         }
24879         if (node.hasAttribute('width')) {
24880             node.removeAttribute('width');
24881         }
24882         
24883          
24884         if (node.hasAttribute("style")) {
24885             // pretty basic...
24886             
24887             var styles = node.getAttribute("style").split(";");
24888             var nstyle = [];
24889             Roo.each(styles, function(s) {
24890                 if (!s.match(/:/)) {
24891                     return;
24892                 }
24893                 var kv = s.split(":");
24894                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
24895                     return;
24896                 }
24897                 // what ever is left... we allow.
24898                 nstyle.push(s);
24899             });
24900             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24901             if (!nstyle.length) {
24902                 node.removeAttribute('style');
24903             }
24904         }
24905         
24906         this.iterateChildren(node, this.cleanTableWidths);
24907         
24908         
24909     },
24910     
24911     
24912     
24913     
24914     domToHTML : function(currentElement, depth, nopadtext) {
24915         
24916         depth = depth || 0;
24917         nopadtext = nopadtext || false;
24918     
24919         if (!currentElement) {
24920             return this.domToHTML(this.doc.body);
24921         }
24922         
24923         //Roo.log(currentElement);
24924         var j;
24925         var allText = false;
24926         var nodeName = currentElement.nodeName;
24927         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
24928         
24929         if  (nodeName == '#text') {
24930             
24931             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
24932         }
24933         
24934         
24935         var ret = '';
24936         if (nodeName != 'BODY') {
24937              
24938             var i = 0;
24939             // Prints the node tagName, such as <A>, <IMG>, etc
24940             if (tagName) {
24941                 var attr = [];
24942                 for(i = 0; i < currentElement.attributes.length;i++) {
24943                     // quoting?
24944                     var aname = currentElement.attributes.item(i).name;
24945                     if (!currentElement.attributes.item(i).value.length) {
24946                         continue;
24947                     }
24948                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
24949                 }
24950                 
24951                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
24952             } 
24953             else {
24954                 
24955                 // eack
24956             }
24957         } else {
24958             tagName = false;
24959         }
24960         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
24961             return ret;
24962         }
24963         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
24964             nopadtext = true;
24965         }
24966         
24967         
24968         // Traverse the tree
24969         i = 0;
24970         var currentElementChild = currentElement.childNodes.item(i);
24971         var allText = true;
24972         var innerHTML  = '';
24973         lastnode = '';
24974         while (currentElementChild) {
24975             // Formatting code (indent the tree so it looks nice on the screen)
24976             var nopad = nopadtext;
24977             if (lastnode == 'SPAN') {
24978                 nopad  = true;
24979             }
24980             // text
24981             if  (currentElementChild.nodeName == '#text') {
24982                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
24983                 toadd = nopadtext ? toadd : toadd.trim();
24984                 if (!nopad && toadd.length > 80) {
24985                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
24986                 }
24987                 innerHTML  += toadd;
24988                 
24989                 i++;
24990                 currentElementChild = currentElement.childNodes.item(i);
24991                 lastNode = '';
24992                 continue;
24993             }
24994             allText = false;
24995             
24996             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
24997                 
24998             // Recursively traverse the tree structure of the child node
24999             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25000             lastnode = currentElementChild.nodeName;
25001             i++;
25002             currentElementChild=currentElement.childNodes.item(i);
25003         }
25004         
25005         ret += innerHTML;
25006         
25007         if (!allText) {
25008                 // The remaining code is mostly for formatting the tree
25009             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25010         }
25011         
25012         
25013         if (tagName) {
25014             ret+= "</"+tagName+">";
25015         }
25016         return ret;
25017         
25018     },
25019         
25020     applyBlacklists : function()
25021     {
25022         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25023         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25024         
25025         this.white = [];
25026         this.black = [];
25027         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25028             if (b.indexOf(tag) > -1) {
25029                 return;
25030             }
25031             this.white.push(tag);
25032             
25033         }, this);
25034         
25035         Roo.each(w, function(tag) {
25036             if (b.indexOf(tag) > -1) {
25037                 return;
25038             }
25039             if (this.white.indexOf(tag) > -1) {
25040                 return;
25041             }
25042             this.white.push(tag);
25043             
25044         }, this);
25045         
25046         
25047         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25048             if (w.indexOf(tag) > -1) {
25049                 return;
25050             }
25051             this.black.push(tag);
25052             
25053         }, this);
25054         
25055         Roo.each(b, function(tag) {
25056             if (w.indexOf(tag) > -1) {
25057                 return;
25058             }
25059             if (this.black.indexOf(tag) > -1) {
25060                 return;
25061             }
25062             this.black.push(tag);
25063             
25064         }, this);
25065         
25066         
25067         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25068         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25069         
25070         this.cwhite = [];
25071         this.cblack = [];
25072         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25073             if (b.indexOf(tag) > -1) {
25074                 return;
25075             }
25076             this.cwhite.push(tag);
25077             
25078         }, this);
25079         
25080         Roo.each(w, function(tag) {
25081             if (b.indexOf(tag) > -1) {
25082                 return;
25083             }
25084             if (this.cwhite.indexOf(tag) > -1) {
25085                 return;
25086             }
25087             this.cwhite.push(tag);
25088             
25089         }, this);
25090         
25091         
25092         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25093             if (w.indexOf(tag) > -1) {
25094                 return;
25095             }
25096             this.cblack.push(tag);
25097             
25098         }, this);
25099         
25100         Roo.each(b, function(tag) {
25101             if (w.indexOf(tag) > -1) {
25102                 return;
25103             }
25104             if (this.cblack.indexOf(tag) > -1) {
25105                 return;
25106             }
25107             this.cblack.push(tag);
25108             
25109         }, this);
25110     },
25111     
25112     setStylesheets : function(stylesheets)
25113     {
25114         if(typeof(stylesheets) == 'string'){
25115             Roo.get(this.iframe.contentDocument.head).createChild({
25116                 tag : 'link',
25117                 rel : 'stylesheet',
25118                 type : 'text/css',
25119                 href : stylesheets
25120             });
25121             
25122             return;
25123         }
25124         var _this = this;
25125      
25126         Roo.each(stylesheets, function(s) {
25127             if(!s.length){
25128                 return;
25129             }
25130             
25131             Roo.get(_this.iframe.contentDocument.head).createChild({
25132                 tag : 'link',
25133                 rel : 'stylesheet',
25134                 type : 'text/css',
25135                 href : s
25136             });
25137         });
25138
25139         
25140     },
25141     
25142     removeStylesheets : function()
25143     {
25144         var _this = this;
25145         
25146         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25147             s.remove();
25148         });
25149     },
25150     
25151     setStyle : function(style)
25152     {
25153         Roo.get(this.iframe.contentDocument.head).createChild({
25154             tag : 'style',
25155             type : 'text/css',
25156             html : style
25157         });
25158
25159         return;
25160     }
25161     
25162     // hide stuff that is not compatible
25163     /**
25164      * @event blur
25165      * @hide
25166      */
25167     /**
25168      * @event change
25169      * @hide
25170      */
25171     /**
25172      * @event focus
25173      * @hide
25174      */
25175     /**
25176      * @event specialkey
25177      * @hide
25178      */
25179     /**
25180      * @cfg {String} fieldClass @hide
25181      */
25182     /**
25183      * @cfg {String} focusClass @hide
25184      */
25185     /**
25186      * @cfg {String} autoCreate @hide
25187      */
25188     /**
25189      * @cfg {String} inputType @hide
25190      */
25191     /**
25192      * @cfg {String} invalidClass @hide
25193      */
25194     /**
25195      * @cfg {String} invalidText @hide
25196      */
25197     /**
25198      * @cfg {String} msgFx @hide
25199      */
25200     /**
25201      * @cfg {String} validateOnBlur @hide
25202      */
25203 });
25204
25205 Roo.HtmlEditorCore.white = [
25206         'area', 'br', 'img', 'input', 'hr', 'wbr',
25207         
25208        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25209        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25210        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25211        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25212        'table',   'ul',         'xmp', 
25213        
25214        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25215       'thead',   'tr', 
25216      
25217       'dir', 'menu', 'ol', 'ul', 'dl',
25218        
25219       'embed',  'object'
25220 ];
25221
25222
25223 Roo.HtmlEditorCore.black = [
25224     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25225         'applet', // 
25226         'base',   'basefont', 'bgsound', 'blink',  'body', 
25227         'frame',  'frameset', 'head',    'html',   'ilayer', 
25228         'iframe', 'layer',  'link',     'meta',    'object',   
25229         'script', 'style' ,'title',  'xml' // clean later..
25230 ];
25231 Roo.HtmlEditorCore.clean = [
25232     'script', 'style', 'title', 'xml'
25233 ];
25234 Roo.HtmlEditorCore.remove = [
25235     'font'
25236 ];
25237 // attributes..
25238
25239 Roo.HtmlEditorCore.ablack = [
25240     'on'
25241 ];
25242     
25243 Roo.HtmlEditorCore.aclean = [ 
25244     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25245 ];
25246
25247 // protocols..
25248 Roo.HtmlEditorCore.pwhite= [
25249         'http',  'https',  'mailto'
25250 ];
25251
25252 // white listed style attributes.
25253 Roo.HtmlEditorCore.cwhite= [
25254       //  'text-align', /// default is to allow most things..
25255       
25256          
25257 //        'font-size'//??
25258 ];
25259
25260 // black listed style attributes.
25261 Roo.HtmlEditorCore.cblack= [
25262       //  'font-size' -- this can be set by the project 
25263 ];
25264
25265
25266 Roo.HtmlEditorCore.swapCodes   =[ 
25267     [    8211, "--" ], 
25268     [    8212, "--" ], 
25269     [    8216,  "'" ],  
25270     [    8217, "'" ],  
25271     [    8220, '"' ],  
25272     [    8221, '"' ],  
25273     [    8226, "*" ],  
25274     [    8230, "..." ]
25275 ]; 
25276
25277     /*
25278  * - LGPL
25279  *
25280  * HtmlEditor
25281  * 
25282  */
25283
25284 /**
25285  * @class Roo.bootstrap.HtmlEditor
25286  * @extends Roo.bootstrap.TextArea
25287  * Bootstrap HtmlEditor class
25288
25289  * @constructor
25290  * Create a new HtmlEditor
25291  * @param {Object} config The config object
25292  */
25293
25294 Roo.bootstrap.HtmlEditor = function(config){
25295     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25296     if (!this.toolbars) {
25297         this.toolbars = [];
25298     }
25299     
25300     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25301     this.addEvents({
25302             /**
25303              * @event initialize
25304              * Fires when the editor is fully initialized (including the iframe)
25305              * @param {HtmlEditor} this
25306              */
25307             initialize: true,
25308             /**
25309              * @event activate
25310              * Fires when the editor is first receives the focus. Any insertion must wait
25311              * until after this event.
25312              * @param {HtmlEditor} this
25313              */
25314             activate: true,
25315              /**
25316              * @event beforesync
25317              * Fires before the textarea is updated with content from the editor iframe. Return false
25318              * to cancel the sync.
25319              * @param {HtmlEditor} this
25320              * @param {String} html
25321              */
25322             beforesync: true,
25323              /**
25324              * @event beforepush
25325              * Fires before the iframe editor is updated with content from the textarea. Return false
25326              * to cancel the push.
25327              * @param {HtmlEditor} this
25328              * @param {String} html
25329              */
25330             beforepush: true,
25331              /**
25332              * @event sync
25333              * Fires when the textarea is updated with content from the editor iframe.
25334              * @param {HtmlEditor} this
25335              * @param {String} html
25336              */
25337             sync: true,
25338              /**
25339              * @event push
25340              * Fires when the iframe editor is updated with content from the textarea.
25341              * @param {HtmlEditor} this
25342              * @param {String} html
25343              */
25344             push: true,
25345              /**
25346              * @event editmodechange
25347              * Fires when the editor switches edit modes
25348              * @param {HtmlEditor} this
25349              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25350              */
25351             editmodechange: true,
25352             /**
25353              * @event editorevent
25354              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25355              * @param {HtmlEditor} this
25356              */
25357             editorevent: true,
25358             /**
25359              * @event firstfocus
25360              * Fires when on first focus - needed by toolbars..
25361              * @param {HtmlEditor} this
25362              */
25363             firstfocus: true,
25364             /**
25365              * @event autosave
25366              * Auto save the htmlEditor value as a file into Events
25367              * @param {HtmlEditor} this
25368              */
25369             autosave: true,
25370             /**
25371              * @event savedpreview
25372              * preview the saved version of htmlEditor
25373              * @param {HtmlEditor} this
25374              */
25375             savedpreview: true
25376         });
25377 };
25378
25379
25380 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25381     
25382     
25383       /**
25384      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25385      */
25386     toolbars : false,
25387     
25388      /**
25389     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25390     */
25391     btns : [],
25392    
25393      /**
25394      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25395      *                        Roo.resizable.
25396      */
25397     resizable : false,
25398      /**
25399      * @cfg {Number} height (in pixels)
25400      */   
25401     height: 300,
25402    /**
25403      * @cfg {Number} width (in pixels)
25404      */   
25405     width: false,
25406     
25407     /**
25408      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25409      * 
25410      */
25411     stylesheets: false,
25412     
25413     // id of frame..
25414     frameId: false,
25415     
25416     // private properties
25417     validationEvent : false,
25418     deferHeight: true,
25419     initialized : false,
25420     activated : false,
25421     
25422     onFocus : Roo.emptyFn,
25423     iframePad:3,
25424     hideMode:'offsets',
25425     
25426     tbContainer : false,
25427     
25428     bodyCls : '',
25429     
25430     toolbarContainer :function() {
25431         return this.wrap.select('.x-html-editor-tb',true).first();
25432     },
25433
25434     /**
25435      * Protected method that will not generally be called directly. It
25436      * is called when the editor creates its toolbar. Override this method if you need to
25437      * add custom toolbar buttons.
25438      * @param {HtmlEditor} editor
25439      */
25440     createToolbar : function(){
25441         Roo.log('renewing');
25442         Roo.log("create toolbars");
25443         
25444         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25445         this.toolbars[0].render(this.toolbarContainer());
25446         
25447         return;
25448         
25449 //        if (!editor.toolbars || !editor.toolbars.length) {
25450 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25451 //        }
25452 //        
25453 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25454 //            editor.toolbars[i] = Roo.factory(
25455 //                    typeof(editor.toolbars[i]) == 'string' ?
25456 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25457 //                Roo.bootstrap.HtmlEditor);
25458 //            editor.toolbars[i].init(editor);
25459 //        }
25460     },
25461
25462      
25463     // private
25464     onRender : function(ct, position)
25465     {
25466        // Roo.log("Call onRender: " + this.xtype);
25467         var _t = this;
25468         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25469       
25470         this.wrap = this.inputEl().wrap({
25471             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25472         });
25473         
25474         this.editorcore.onRender(ct, position);
25475          
25476         if (this.resizable) {
25477             this.resizeEl = new Roo.Resizable(this.wrap, {
25478                 pinned : true,
25479                 wrap: true,
25480                 dynamic : true,
25481                 minHeight : this.height,
25482                 height: this.height,
25483                 handles : this.resizable,
25484                 width: this.width,
25485                 listeners : {
25486                     resize : function(r, w, h) {
25487                         _t.onResize(w,h); // -something
25488                     }
25489                 }
25490             });
25491             
25492         }
25493         this.createToolbar(this);
25494        
25495         
25496         if(!this.width && this.resizable){
25497             this.setSize(this.wrap.getSize());
25498         }
25499         if (this.resizeEl) {
25500             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25501             // should trigger onReize..
25502         }
25503         
25504     },
25505
25506     // private
25507     onResize : function(w, h)
25508     {
25509         Roo.log('resize: ' +w + ',' + h );
25510         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25511         var ew = false;
25512         var eh = false;
25513         
25514         if(this.inputEl() ){
25515             if(typeof w == 'number'){
25516                 var aw = w - this.wrap.getFrameWidth('lr');
25517                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25518                 ew = aw;
25519             }
25520             if(typeof h == 'number'){
25521                  var tbh = -11;  // fixme it needs to tool bar size!
25522                 for (var i =0; i < this.toolbars.length;i++) {
25523                     // fixme - ask toolbars for heights?
25524                     tbh += this.toolbars[i].el.getHeight();
25525                     //if (this.toolbars[i].footer) {
25526                     //    tbh += this.toolbars[i].footer.el.getHeight();
25527                     //}
25528                 }
25529               
25530                 
25531                 
25532                 
25533                 
25534                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25535                 ah -= 5; // knock a few pixes off for look..
25536                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25537                 var eh = ah;
25538             }
25539         }
25540         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25541         this.editorcore.onResize(ew,eh);
25542         
25543     },
25544
25545     /**
25546      * Toggles the editor between standard and source edit mode.
25547      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25548      */
25549     toggleSourceEdit : function(sourceEditMode)
25550     {
25551         this.editorcore.toggleSourceEdit(sourceEditMode);
25552         
25553         if(this.editorcore.sourceEditMode){
25554             Roo.log('editor - showing textarea');
25555             
25556 //            Roo.log('in');
25557 //            Roo.log(this.syncValue());
25558             this.syncValue();
25559             this.inputEl().removeClass(['hide', 'x-hidden']);
25560             this.inputEl().dom.removeAttribute('tabIndex');
25561             this.inputEl().focus();
25562         }else{
25563             Roo.log('editor - hiding textarea');
25564 //            Roo.log('out')
25565 //            Roo.log(this.pushValue()); 
25566             this.pushValue();
25567             
25568             this.inputEl().addClass(['hide', 'x-hidden']);
25569             this.inputEl().dom.setAttribute('tabIndex', -1);
25570             //this.deferFocus();
25571         }
25572          
25573         if(this.resizable){
25574             this.setSize(this.wrap.getSize());
25575         }
25576         
25577         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25578     },
25579  
25580     // private (for BoxComponent)
25581     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25582
25583     // private (for BoxComponent)
25584     getResizeEl : function(){
25585         return this.wrap;
25586     },
25587
25588     // private (for BoxComponent)
25589     getPositionEl : function(){
25590         return this.wrap;
25591     },
25592
25593     // private
25594     initEvents : function(){
25595         this.originalValue = this.getValue();
25596     },
25597
25598 //    /**
25599 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25600 //     * @method
25601 //     */
25602 //    markInvalid : Roo.emptyFn,
25603 //    /**
25604 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25605 //     * @method
25606 //     */
25607 //    clearInvalid : Roo.emptyFn,
25608
25609     setValue : function(v){
25610         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25611         this.editorcore.pushValue();
25612     },
25613
25614      
25615     // private
25616     deferFocus : function(){
25617         this.focus.defer(10, this);
25618     },
25619
25620     // doc'ed in Field
25621     focus : function(){
25622         this.editorcore.focus();
25623         
25624     },
25625       
25626
25627     // private
25628     onDestroy : function(){
25629         
25630         
25631         
25632         if(this.rendered){
25633             
25634             for (var i =0; i < this.toolbars.length;i++) {
25635                 // fixme - ask toolbars for heights?
25636                 this.toolbars[i].onDestroy();
25637             }
25638             
25639             this.wrap.dom.innerHTML = '';
25640             this.wrap.remove();
25641         }
25642     },
25643
25644     // private
25645     onFirstFocus : function(){
25646         //Roo.log("onFirstFocus");
25647         this.editorcore.onFirstFocus();
25648          for (var i =0; i < this.toolbars.length;i++) {
25649             this.toolbars[i].onFirstFocus();
25650         }
25651         
25652     },
25653     
25654     // private
25655     syncValue : function()
25656     {   
25657         this.editorcore.syncValue();
25658     },
25659     
25660     pushValue : function()
25661     {   
25662         this.editorcore.pushValue();
25663     }
25664      
25665     
25666     // hide stuff that is not compatible
25667     /**
25668      * @event blur
25669      * @hide
25670      */
25671     /**
25672      * @event change
25673      * @hide
25674      */
25675     /**
25676      * @event focus
25677      * @hide
25678      */
25679     /**
25680      * @event specialkey
25681      * @hide
25682      */
25683     /**
25684      * @cfg {String} fieldClass @hide
25685      */
25686     /**
25687      * @cfg {String} focusClass @hide
25688      */
25689     /**
25690      * @cfg {String} autoCreate @hide
25691      */
25692     /**
25693      * @cfg {String} inputType @hide
25694      */
25695      
25696     /**
25697      * @cfg {String} invalidText @hide
25698      */
25699     /**
25700      * @cfg {String} msgFx @hide
25701      */
25702     /**
25703      * @cfg {String} validateOnBlur @hide
25704      */
25705 });
25706  
25707     
25708    
25709    
25710    
25711       
25712 Roo.namespace('Roo.bootstrap.htmleditor');
25713 /**
25714  * @class Roo.bootstrap.HtmlEditorToolbar1
25715  * Basic Toolbar
25716  * 
25717  * @example
25718  * Usage:
25719  *
25720  new Roo.bootstrap.HtmlEditor({
25721     ....
25722     toolbars : [
25723         new Roo.bootstrap.HtmlEditorToolbar1({
25724             disable : { fonts: 1 , format: 1, ..., ... , ...],
25725             btns : [ .... ]
25726         })
25727     }
25728      
25729  * 
25730  * @cfg {Object} disable List of elements to disable..
25731  * @cfg {Array} btns List of additional buttons.
25732  * 
25733  * 
25734  * NEEDS Extra CSS? 
25735  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25736  */
25737  
25738 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25739 {
25740     
25741     Roo.apply(this, config);
25742     
25743     // default disabled, based on 'good practice'..
25744     this.disable = this.disable || {};
25745     Roo.applyIf(this.disable, {
25746         fontSize : true,
25747         colors : true,
25748         specialElements : true
25749     });
25750     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25751     
25752     this.editor = config.editor;
25753     this.editorcore = config.editor.editorcore;
25754     
25755     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25756     
25757     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25758     // dont call parent... till later.
25759 }
25760 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
25761      
25762     bar : true,
25763     
25764     editor : false,
25765     editorcore : false,
25766     
25767     
25768     formats : [
25769         "p" ,  
25770         "h1","h2","h3","h4","h5","h6", 
25771         "pre", "code", 
25772         "abbr", "acronym", "address", "cite", "samp", "var",
25773         'div','span'
25774     ],
25775     
25776     onRender : function(ct, position)
25777     {
25778        // Roo.log("Call onRender: " + this.xtype);
25779         
25780        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25781        Roo.log(this.el);
25782        this.el.dom.style.marginBottom = '0';
25783        var _this = this;
25784        var editorcore = this.editorcore;
25785        var editor= this.editor;
25786        
25787        var children = [];
25788        var btn = function(id,cmd , toggle, handler, html){
25789        
25790             var  event = toggle ? 'toggle' : 'click';
25791        
25792             var a = {
25793                 size : 'sm',
25794                 xtype: 'Button',
25795                 xns: Roo.bootstrap,
25796                 //glyphicon : id,
25797                 fa: id,
25798                 cmd : id || cmd,
25799                 enableToggle:toggle !== false,
25800                 html : html || '',
25801                 pressed : toggle ? false : null,
25802                 listeners : {}
25803             };
25804             a.listeners[toggle ? 'toggle' : 'click'] = function() {
25805                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
25806             };
25807             children.push(a);
25808             return a;
25809        }
25810        
25811     //    var cb_box = function...
25812         
25813         var style = {
25814                 xtype: 'Button',
25815                 size : 'sm',
25816                 xns: Roo.bootstrap,
25817                 fa : 'font',
25818                 //html : 'submit'
25819                 menu : {
25820                     xtype: 'Menu',
25821                     xns: Roo.bootstrap,
25822                     items:  []
25823                 }
25824         };
25825         Roo.each(this.formats, function(f) {
25826             style.menu.items.push({
25827                 xtype :'MenuItem',
25828                 xns: Roo.bootstrap,
25829                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25830                 tagname : f,
25831                 listeners : {
25832                     click : function()
25833                     {
25834                         editorcore.insertTag(this.tagname);
25835                         editor.focus();
25836                     }
25837                 }
25838                 
25839             });
25840         });
25841         children.push(style);   
25842         
25843         btn('bold',false,true);
25844         btn('italic',false,true);
25845         btn('align-left', 'justifyleft',true);
25846         btn('align-center', 'justifycenter',true);
25847         btn('align-right' , 'justifyright',true);
25848         btn('link', false, false, function(btn) {
25849             //Roo.log("create link?");
25850             var url = prompt(this.createLinkText, this.defaultLinkValue);
25851             if(url && url != 'http:/'+'/'){
25852                 this.editorcore.relayCmd('createlink', url);
25853             }
25854         }),
25855         btn('list','insertunorderedlist',true);
25856         btn('pencil', false,true, function(btn){
25857                 Roo.log(this);
25858                 this.toggleSourceEdit(btn.pressed);
25859         });
25860         
25861         if (this.editor.btns.length > 0) {
25862             for (var i = 0; i<this.editor.btns.length; i++) {
25863                 children.push(this.editor.btns[i]);
25864             }
25865         }
25866         
25867         /*
25868         var cog = {
25869                 xtype: 'Button',
25870                 size : 'sm',
25871                 xns: Roo.bootstrap,
25872                 glyphicon : 'cog',
25873                 //html : 'submit'
25874                 menu : {
25875                     xtype: 'Menu',
25876                     xns: Roo.bootstrap,
25877                     items:  []
25878                 }
25879         };
25880         
25881         cog.menu.items.push({
25882             xtype :'MenuItem',
25883             xns: Roo.bootstrap,
25884             html : Clean styles,
25885             tagname : f,
25886             listeners : {
25887                 click : function()
25888                 {
25889                     editorcore.insertTag(this.tagname);
25890                     editor.focus();
25891                 }
25892             }
25893             
25894         });
25895        */
25896         
25897          
25898        this.xtype = 'NavSimplebar';
25899         
25900         for(var i=0;i< children.length;i++) {
25901             
25902             this.buttons.add(this.addxtypeChild(children[i]));
25903             
25904         }
25905         
25906         editor.on('editorevent', this.updateToolbar, this);
25907     },
25908     onBtnClick : function(id)
25909     {
25910        this.editorcore.relayCmd(id);
25911        this.editorcore.focus();
25912     },
25913     
25914     /**
25915      * Protected method that will not generally be called directly. It triggers
25916      * a toolbar update by reading the markup state of the current selection in the editor.
25917      */
25918     updateToolbar: function(){
25919
25920         if(!this.editorcore.activated){
25921             this.editor.onFirstFocus(); // is this neeed?
25922             return;
25923         }
25924
25925         var btns = this.buttons; 
25926         var doc = this.editorcore.doc;
25927         btns.get('bold').setActive(doc.queryCommandState('bold'));
25928         btns.get('italic').setActive(doc.queryCommandState('italic'));
25929         //btns.get('underline').setActive(doc.queryCommandState('underline'));
25930         
25931         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
25932         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
25933         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
25934         
25935         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
25936         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
25937          /*
25938         
25939         var ans = this.editorcore.getAllAncestors();
25940         if (this.formatCombo) {
25941             
25942             
25943             var store = this.formatCombo.store;
25944             this.formatCombo.setValue("");
25945             for (var i =0; i < ans.length;i++) {
25946                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25947                     // select it..
25948                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25949                     break;
25950                 }
25951             }
25952         }
25953         
25954         
25955         
25956         // hides menus... - so this cant be on a menu...
25957         Roo.bootstrap.MenuMgr.hideAll();
25958         */
25959         Roo.bootstrap.MenuMgr.hideAll();
25960         //this.editorsyncValue();
25961     },
25962     onFirstFocus: function() {
25963         this.buttons.each(function(item){
25964            item.enable();
25965         });
25966     },
25967     toggleSourceEdit : function(sourceEditMode){
25968         
25969           
25970         if(sourceEditMode){
25971             Roo.log("disabling buttons");
25972            this.buttons.each( function(item){
25973                 if(item.cmd != 'pencil'){
25974                     item.disable();
25975                 }
25976             });
25977           
25978         }else{
25979             Roo.log("enabling buttons");
25980             if(this.editorcore.initialized){
25981                 this.buttons.each( function(item){
25982                     item.enable();
25983                 });
25984             }
25985             
25986         }
25987         Roo.log("calling toggole on editor");
25988         // tell the editor that it's been pressed..
25989         this.editor.toggleSourceEdit(sourceEditMode);
25990        
25991     }
25992 });
25993
25994
25995
25996
25997
25998 /**
25999  * @class Roo.bootstrap.Table.AbstractSelectionModel
26000  * @extends Roo.util.Observable
26001  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26002  * implemented by descendant classes.  This class should not be directly instantiated.
26003  * @constructor
26004  */
26005 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26006     this.locked = false;
26007     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26008 };
26009
26010
26011 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26012     /** @ignore Called by the grid automatically. Do not call directly. */
26013     init : function(grid){
26014         this.grid = grid;
26015         this.initEvents();
26016     },
26017
26018     /**
26019      * Locks the selections.
26020      */
26021     lock : function(){
26022         this.locked = true;
26023     },
26024
26025     /**
26026      * Unlocks the selections.
26027      */
26028     unlock : function(){
26029         this.locked = false;
26030     },
26031
26032     /**
26033      * Returns true if the selections are locked.
26034      * @return {Boolean}
26035      */
26036     isLocked : function(){
26037         return this.locked;
26038     },
26039     
26040     
26041     initEvents : function ()
26042     {
26043         
26044     }
26045 });
26046 /**
26047  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26048  * @class Roo.bootstrap.Table.RowSelectionModel
26049  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26050  * It supports multiple selections and keyboard selection/navigation. 
26051  * @constructor
26052  * @param {Object} config
26053  */
26054
26055 Roo.bootstrap.Table.RowSelectionModel = function(config){
26056     Roo.apply(this, config);
26057     this.selections = new Roo.util.MixedCollection(false, function(o){
26058         return o.id;
26059     });
26060
26061     this.last = false;
26062     this.lastActive = false;
26063
26064     this.addEvents({
26065         /**
26066              * @event selectionchange
26067              * Fires when the selection changes
26068              * @param {SelectionModel} this
26069              */
26070             "selectionchange" : true,
26071         /**
26072              * @event afterselectionchange
26073              * Fires after the selection changes (eg. by key press or clicking)
26074              * @param {SelectionModel} this
26075              */
26076             "afterselectionchange" : true,
26077         /**
26078              * @event beforerowselect
26079              * Fires when a row is selected being selected, return false to cancel.
26080              * @param {SelectionModel} this
26081              * @param {Number} rowIndex The selected index
26082              * @param {Boolean} keepExisting False if other selections will be cleared
26083              */
26084             "beforerowselect" : true,
26085         /**
26086              * @event rowselect
26087              * Fires when a row is selected.
26088              * @param {SelectionModel} this
26089              * @param {Number} rowIndex The selected index
26090              * @param {Roo.data.Record} r The record
26091              */
26092             "rowselect" : true,
26093         /**
26094              * @event rowdeselect
26095              * Fires when a row is deselected.
26096              * @param {SelectionModel} this
26097              * @param {Number} rowIndex The selected index
26098              */
26099         "rowdeselect" : true
26100     });
26101     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26102     this.locked = false;
26103  };
26104
26105 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26106     /**
26107      * @cfg {Boolean} singleSelect
26108      * True to allow selection of only one row at a time (defaults to false)
26109      */
26110     singleSelect : false,
26111
26112     // private
26113     initEvents : function()
26114     {
26115
26116         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26117         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26118         //}else{ // allow click to work like normal
26119          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26120         //}
26121         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26122         this.grid.on("rowclick", this.handleMouseDown, this);
26123         
26124         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26125             "up" : function(e){
26126                 if(!e.shiftKey){
26127                     this.selectPrevious(e.shiftKey);
26128                 }else if(this.last !== false && this.lastActive !== false){
26129                     var last = this.last;
26130                     this.selectRange(this.last,  this.lastActive-1);
26131                     this.grid.getView().focusRow(this.lastActive);
26132                     if(last !== false){
26133                         this.last = last;
26134                     }
26135                 }else{
26136                     this.selectFirstRow();
26137                 }
26138                 this.fireEvent("afterselectionchange", this);
26139             },
26140             "down" : function(e){
26141                 if(!e.shiftKey){
26142                     this.selectNext(e.shiftKey);
26143                 }else if(this.last !== false && this.lastActive !== false){
26144                     var last = this.last;
26145                     this.selectRange(this.last,  this.lastActive+1);
26146                     this.grid.getView().focusRow(this.lastActive);
26147                     if(last !== false){
26148                         this.last = last;
26149                     }
26150                 }else{
26151                     this.selectFirstRow();
26152                 }
26153                 this.fireEvent("afterselectionchange", this);
26154             },
26155             scope: this
26156         });
26157         this.grid.store.on('load', function(){
26158             this.selections.clear();
26159         },this);
26160         /*
26161         var view = this.grid.view;
26162         view.on("refresh", this.onRefresh, this);
26163         view.on("rowupdated", this.onRowUpdated, this);
26164         view.on("rowremoved", this.onRemove, this);
26165         */
26166     },
26167
26168     // private
26169     onRefresh : function()
26170     {
26171         var ds = this.grid.store, i, v = this.grid.view;
26172         var s = this.selections;
26173         s.each(function(r){
26174             if((i = ds.indexOfId(r.id)) != -1){
26175                 v.onRowSelect(i);
26176             }else{
26177                 s.remove(r);
26178             }
26179         });
26180     },
26181
26182     // private
26183     onRemove : function(v, index, r){
26184         this.selections.remove(r);
26185     },
26186
26187     // private
26188     onRowUpdated : function(v, index, r){
26189         if(this.isSelected(r)){
26190             v.onRowSelect(index);
26191         }
26192     },
26193
26194     /**
26195      * Select records.
26196      * @param {Array} records The records to select
26197      * @param {Boolean} keepExisting (optional) True to keep existing selections
26198      */
26199     selectRecords : function(records, keepExisting)
26200     {
26201         if(!keepExisting){
26202             this.clearSelections();
26203         }
26204             var ds = this.grid.store;
26205         for(var i = 0, len = records.length; i < len; i++){
26206             this.selectRow(ds.indexOf(records[i]), true);
26207         }
26208     },
26209
26210     /**
26211      * Gets the number of selected rows.
26212      * @return {Number}
26213      */
26214     getCount : function(){
26215         return this.selections.length;
26216     },
26217
26218     /**
26219      * Selects the first row in the grid.
26220      */
26221     selectFirstRow : function(){
26222         this.selectRow(0);
26223     },
26224
26225     /**
26226      * Select the last row.
26227      * @param {Boolean} keepExisting (optional) True to keep existing selections
26228      */
26229     selectLastRow : function(keepExisting){
26230         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26231         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26232     },
26233
26234     /**
26235      * Selects the row immediately following the last selected row.
26236      * @param {Boolean} keepExisting (optional) True to keep existing selections
26237      */
26238     selectNext : function(keepExisting)
26239     {
26240             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26241             this.selectRow(this.last+1, keepExisting);
26242             this.grid.getView().focusRow(this.last);
26243         }
26244     },
26245
26246     /**
26247      * Selects the row that precedes the last selected row.
26248      * @param {Boolean} keepExisting (optional) True to keep existing selections
26249      */
26250     selectPrevious : function(keepExisting){
26251         if(this.last){
26252             this.selectRow(this.last-1, keepExisting);
26253             this.grid.getView().focusRow(this.last);
26254         }
26255     },
26256
26257     /**
26258      * Returns the selected records
26259      * @return {Array} Array of selected records
26260      */
26261     getSelections : function(){
26262         return [].concat(this.selections.items);
26263     },
26264
26265     /**
26266      * Returns the first selected record.
26267      * @return {Record}
26268      */
26269     getSelected : function(){
26270         return this.selections.itemAt(0);
26271     },
26272
26273
26274     /**
26275      * Clears all selections.
26276      */
26277     clearSelections : function(fast)
26278     {
26279         if(this.locked) {
26280             return;
26281         }
26282         if(fast !== true){
26283                 var ds = this.grid.store;
26284             var s = this.selections;
26285             s.each(function(r){
26286                 this.deselectRow(ds.indexOfId(r.id));
26287             }, this);
26288             s.clear();
26289         }else{
26290             this.selections.clear();
26291         }
26292         this.last = false;
26293     },
26294
26295
26296     /**
26297      * Selects all rows.
26298      */
26299     selectAll : function(){
26300         if(this.locked) {
26301             return;
26302         }
26303         this.selections.clear();
26304         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26305             this.selectRow(i, true);
26306         }
26307     },
26308
26309     /**
26310      * Returns True if there is a selection.
26311      * @return {Boolean}
26312      */
26313     hasSelection : function(){
26314         return this.selections.length > 0;
26315     },
26316
26317     /**
26318      * Returns True if the specified row is selected.
26319      * @param {Number/Record} record The record or index of the record to check
26320      * @return {Boolean}
26321      */
26322     isSelected : function(index){
26323             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26324         return (r && this.selections.key(r.id) ? true : false);
26325     },
26326
26327     /**
26328      * Returns True if the specified record id is selected.
26329      * @param {String} id The id of record to check
26330      * @return {Boolean}
26331      */
26332     isIdSelected : function(id){
26333         return (this.selections.key(id) ? true : false);
26334     },
26335
26336
26337     // private
26338     handleMouseDBClick : function(e, t){
26339         
26340     },
26341     // private
26342     handleMouseDown : function(e, t)
26343     {
26344             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26345         if(this.isLocked() || rowIndex < 0 ){
26346             return;
26347         };
26348         if(e.shiftKey && this.last !== false){
26349             var last = this.last;
26350             this.selectRange(last, rowIndex, e.ctrlKey);
26351             this.last = last; // reset the last
26352             t.focus();
26353     
26354         }else{
26355             var isSelected = this.isSelected(rowIndex);
26356             //Roo.log("select row:" + rowIndex);
26357             if(isSelected){
26358                 this.deselectRow(rowIndex);
26359             } else {
26360                         this.selectRow(rowIndex, true);
26361             }
26362     
26363             /*
26364                 if(e.button !== 0 && isSelected){
26365                 alert('rowIndex 2: ' + rowIndex);
26366                     view.focusRow(rowIndex);
26367                 }else if(e.ctrlKey && isSelected){
26368                     this.deselectRow(rowIndex);
26369                 }else if(!isSelected){
26370                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26371                     view.focusRow(rowIndex);
26372                 }
26373             */
26374         }
26375         this.fireEvent("afterselectionchange", this);
26376     },
26377     // private
26378     handleDragableRowClick :  function(grid, rowIndex, e) 
26379     {
26380         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26381             this.selectRow(rowIndex, false);
26382             grid.view.focusRow(rowIndex);
26383              this.fireEvent("afterselectionchange", this);
26384         }
26385     },
26386     
26387     /**
26388      * Selects multiple rows.
26389      * @param {Array} rows Array of the indexes of the row to select
26390      * @param {Boolean} keepExisting (optional) True to keep existing selections
26391      */
26392     selectRows : function(rows, keepExisting){
26393         if(!keepExisting){
26394             this.clearSelections();
26395         }
26396         for(var i = 0, len = rows.length; i < len; i++){
26397             this.selectRow(rows[i], true);
26398         }
26399     },
26400
26401     /**
26402      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26403      * @param {Number} startRow The index of the first row in the range
26404      * @param {Number} endRow The index of the last row in the range
26405      * @param {Boolean} keepExisting (optional) True to retain existing selections
26406      */
26407     selectRange : function(startRow, endRow, keepExisting){
26408         if(this.locked) {
26409             return;
26410         }
26411         if(!keepExisting){
26412             this.clearSelections();
26413         }
26414         if(startRow <= endRow){
26415             for(var i = startRow; i <= endRow; i++){
26416                 this.selectRow(i, true);
26417             }
26418         }else{
26419             for(var i = startRow; i >= endRow; i--){
26420                 this.selectRow(i, true);
26421             }
26422         }
26423     },
26424
26425     /**
26426      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26427      * @param {Number} startRow The index of the first row in the range
26428      * @param {Number} endRow The index of the last row in the range
26429      */
26430     deselectRange : function(startRow, endRow, preventViewNotify){
26431         if(this.locked) {
26432             return;
26433         }
26434         for(var i = startRow; i <= endRow; i++){
26435             this.deselectRow(i, preventViewNotify);
26436         }
26437     },
26438
26439     /**
26440      * Selects a row.
26441      * @param {Number} row The index of the row to select
26442      * @param {Boolean} keepExisting (optional) True to keep existing selections
26443      */
26444     selectRow : function(index, keepExisting, preventViewNotify)
26445     {
26446             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26447             return;
26448         }
26449         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26450             if(!keepExisting || this.singleSelect){
26451                 this.clearSelections();
26452             }
26453             
26454             var r = this.grid.store.getAt(index);
26455             //console.log('selectRow - record id :' + r.id);
26456             
26457             this.selections.add(r);
26458             this.last = this.lastActive = index;
26459             if(!preventViewNotify){
26460                 var proxy = new Roo.Element(
26461                                 this.grid.getRowDom(index)
26462                 );
26463                 proxy.addClass('bg-info info');
26464             }
26465             this.fireEvent("rowselect", this, index, r);
26466             this.fireEvent("selectionchange", this);
26467         }
26468     },
26469
26470     /**
26471      * Deselects a row.
26472      * @param {Number} row The index of the row to deselect
26473      */
26474     deselectRow : function(index, preventViewNotify)
26475     {
26476         if(this.locked) {
26477             return;
26478         }
26479         if(this.last == index){
26480             this.last = false;
26481         }
26482         if(this.lastActive == index){
26483             this.lastActive = false;
26484         }
26485         
26486         var r = this.grid.store.getAt(index);
26487         if (!r) {
26488             return;
26489         }
26490         
26491         this.selections.remove(r);
26492         //.console.log('deselectRow - record id :' + r.id);
26493         if(!preventViewNotify){
26494         
26495             var proxy = new Roo.Element(
26496                 this.grid.getRowDom(index)
26497             );
26498             proxy.removeClass('bg-info info');
26499         }
26500         this.fireEvent("rowdeselect", this, index);
26501         this.fireEvent("selectionchange", this);
26502     },
26503
26504     // private
26505     restoreLast : function(){
26506         if(this._last){
26507             this.last = this._last;
26508         }
26509     },
26510
26511     // private
26512     acceptsNav : function(row, col, cm){
26513         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26514     },
26515
26516     // private
26517     onEditorKey : function(field, e){
26518         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26519         if(k == e.TAB){
26520             e.stopEvent();
26521             ed.completeEdit();
26522             if(e.shiftKey){
26523                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26524             }else{
26525                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26526             }
26527         }else if(k == e.ENTER && !e.ctrlKey){
26528             e.stopEvent();
26529             ed.completeEdit();
26530             if(e.shiftKey){
26531                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26532             }else{
26533                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26534             }
26535         }else if(k == e.ESC){
26536             ed.cancelEdit();
26537         }
26538         if(newCell){
26539             g.startEditing(newCell[0], newCell[1]);
26540         }
26541     }
26542 });
26543 /*
26544  * Based on:
26545  * Ext JS Library 1.1.1
26546  * Copyright(c) 2006-2007, Ext JS, LLC.
26547  *
26548  * Originally Released Under LGPL - original licence link has changed is not relivant.
26549  *
26550  * Fork - LGPL
26551  * <script type="text/javascript">
26552  */
26553  
26554 /**
26555  * @class Roo.bootstrap.PagingToolbar
26556  * @extends Roo.bootstrap.NavSimplebar
26557  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26558  * @constructor
26559  * Create a new PagingToolbar
26560  * @param {Object} config The config object
26561  * @param {Roo.data.Store} store
26562  */
26563 Roo.bootstrap.PagingToolbar = function(config)
26564 {
26565     // old args format still supported... - xtype is prefered..
26566         // created from xtype...
26567     
26568     this.ds = config.dataSource;
26569     
26570     if (config.store && !this.ds) {
26571         this.store= Roo.factory(config.store, Roo.data);
26572         this.ds = this.store;
26573         this.ds.xmodule = this.xmodule || false;
26574     }
26575     
26576     this.toolbarItems = [];
26577     if (config.items) {
26578         this.toolbarItems = config.items;
26579     }
26580     
26581     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26582     
26583     this.cursor = 0;
26584     
26585     if (this.ds) { 
26586         this.bind(this.ds);
26587     }
26588     
26589     if (Roo.bootstrap.version == 4) {
26590         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26591     } else {
26592         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26593     }
26594     
26595 };
26596
26597 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26598     /**
26599      * @cfg {Roo.data.Store} dataSource
26600      * The underlying data store providing the paged data
26601      */
26602     /**
26603      * @cfg {String/HTMLElement/Element} container
26604      * container The id or element that will contain the toolbar
26605      */
26606     /**
26607      * @cfg {Boolean} displayInfo
26608      * True to display the displayMsg (defaults to false)
26609      */
26610     /**
26611      * @cfg {Number} pageSize
26612      * The number of records to display per page (defaults to 20)
26613      */
26614     pageSize: 20,
26615     /**
26616      * @cfg {String} displayMsg
26617      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26618      */
26619     displayMsg : 'Displaying {0} - {1} of {2}',
26620     /**
26621      * @cfg {String} emptyMsg
26622      * The message to display when no records are found (defaults to "No data to display")
26623      */
26624     emptyMsg : 'No data to display',
26625     /**
26626      * Customizable piece of the default paging text (defaults to "Page")
26627      * @type String
26628      */
26629     beforePageText : "Page",
26630     /**
26631      * Customizable piece of the default paging text (defaults to "of %0")
26632      * @type String
26633      */
26634     afterPageText : "of {0}",
26635     /**
26636      * Customizable piece of the default paging text (defaults to "First Page")
26637      * @type String
26638      */
26639     firstText : "First Page",
26640     /**
26641      * Customizable piece of the default paging text (defaults to "Previous Page")
26642      * @type String
26643      */
26644     prevText : "Previous Page",
26645     /**
26646      * Customizable piece of the default paging text (defaults to "Next Page")
26647      * @type String
26648      */
26649     nextText : "Next Page",
26650     /**
26651      * Customizable piece of the default paging text (defaults to "Last Page")
26652      * @type String
26653      */
26654     lastText : "Last Page",
26655     /**
26656      * Customizable piece of the default paging text (defaults to "Refresh")
26657      * @type String
26658      */
26659     refreshText : "Refresh",
26660
26661     buttons : false,
26662     // private
26663     onRender : function(ct, position) 
26664     {
26665         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26666         this.navgroup.parentId = this.id;
26667         this.navgroup.onRender(this.el, null);
26668         // add the buttons to the navgroup
26669         
26670         if(this.displayInfo){
26671             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26672             this.displayEl = this.el.select('.x-paging-info', true).first();
26673 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26674 //            this.displayEl = navel.el.select('span',true).first();
26675         }
26676         
26677         var _this = this;
26678         
26679         if(this.buttons){
26680             Roo.each(_this.buttons, function(e){ // this might need to use render????
26681                Roo.factory(e).render(_this.el);
26682             });
26683         }
26684             
26685         Roo.each(_this.toolbarItems, function(e) {
26686             _this.navgroup.addItem(e);
26687         });
26688         
26689         
26690         this.first = this.navgroup.addItem({
26691             tooltip: this.firstText,
26692             cls: "prev btn-outline-secondary",
26693             html : ' <i class="fa fa-step-backward"></i>',
26694             disabled: true,
26695             preventDefault: true,
26696             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26697         });
26698         
26699         this.prev =  this.navgroup.addItem({
26700             tooltip: this.prevText,
26701             cls: "prev btn-outline-secondary",
26702             html : ' <i class="fa fa-backward"></i>',
26703             disabled: true,
26704             preventDefault: true,
26705             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
26706         });
26707     //this.addSeparator();
26708         
26709         
26710         var field = this.navgroup.addItem( {
26711             tagtype : 'span',
26712             cls : 'x-paging-position  btn-outline-secondary',
26713              disabled: true,
26714             html : this.beforePageText  +
26715                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26716                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
26717          } ); //?? escaped?
26718         
26719         this.field = field.el.select('input', true).first();
26720         this.field.on("keydown", this.onPagingKeydown, this);
26721         this.field.on("focus", function(){this.dom.select();});
26722     
26723     
26724         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
26725         //this.field.setHeight(18);
26726         //this.addSeparator();
26727         this.next = this.navgroup.addItem({
26728             tooltip: this.nextText,
26729             cls: "next btn-outline-secondary",
26730             html : ' <i class="fa fa-forward"></i>',
26731             disabled: true,
26732             preventDefault: true,
26733             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
26734         });
26735         this.last = this.navgroup.addItem({
26736             tooltip: this.lastText,
26737             html : ' <i class="fa fa-step-forward"></i>',
26738             cls: "next btn-outline-secondary",
26739             disabled: true,
26740             preventDefault: true,
26741             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
26742         });
26743     //this.addSeparator();
26744         this.loading = this.navgroup.addItem({
26745             tooltip: this.refreshText,
26746             cls: "btn-outline-secondary",
26747             html : ' <i class="fa fa-refresh"></i>',
26748             preventDefault: true,
26749             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26750         });
26751         
26752     },
26753
26754     // private
26755     updateInfo : function(){
26756         if(this.displayEl){
26757             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26758             var msg = count == 0 ?
26759                 this.emptyMsg :
26760                 String.format(
26761                     this.displayMsg,
26762                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
26763                 );
26764             this.displayEl.update(msg);
26765         }
26766     },
26767
26768     // private
26769     onLoad : function(ds, r, o)
26770     {
26771         this.cursor = o.params.start ? o.params.start : 0;
26772         
26773         var d = this.getPageData(),
26774             ap = d.activePage,
26775             ps = d.pages;
26776         
26777         
26778         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26779         this.field.dom.value = ap;
26780         this.first.setDisabled(ap == 1);
26781         this.prev.setDisabled(ap == 1);
26782         this.next.setDisabled(ap == ps);
26783         this.last.setDisabled(ap == ps);
26784         this.loading.enable();
26785         this.updateInfo();
26786     },
26787
26788     // private
26789     getPageData : function(){
26790         var total = this.ds.getTotalCount();
26791         return {
26792             total : total,
26793             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26794             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
26795         };
26796     },
26797
26798     // private
26799     onLoadError : function(){
26800         this.loading.enable();
26801     },
26802
26803     // private
26804     onPagingKeydown : function(e){
26805         var k = e.getKey();
26806         var d = this.getPageData();
26807         if(k == e.RETURN){
26808             var v = this.field.dom.value, pageNum;
26809             if(!v || isNaN(pageNum = parseInt(v, 10))){
26810                 this.field.dom.value = d.activePage;
26811                 return;
26812             }
26813             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
26814             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26815             e.stopEvent();
26816         }
26817         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))
26818         {
26819           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
26820           this.field.dom.value = pageNum;
26821           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
26822           e.stopEvent();
26823         }
26824         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
26825         {
26826           var v = this.field.dom.value, pageNum; 
26827           var increment = (e.shiftKey) ? 10 : 1;
26828           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
26829                 increment *= -1;
26830           }
26831           if(!v || isNaN(pageNum = parseInt(v, 10))) {
26832             this.field.dom.value = d.activePage;
26833             return;
26834           }
26835           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
26836           {
26837             this.field.dom.value = parseInt(v, 10) + increment;
26838             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
26839             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26840           }
26841           e.stopEvent();
26842         }
26843     },
26844
26845     // private
26846     beforeLoad : function(){
26847         if(this.loading){
26848             this.loading.disable();
26849         }
26850     },
26851
26852     // private
26853     onClick : function(which){
26854         
26855         var ds = this.ds;
26856         if (!ds) {
26857             return;
26858         }
26859         
26860         switch(which){
26861             case "first":
26862                 ds.load({params:{start: 0, limit: this.pageSize}});
26863             break;
26864             case "prev":
26865                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
26866             break;
26867             case "next":
26868                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
26869             break;
26870             case "last":
26871                 var total = ds.getTotalCount();
26872                 var extra = total % this.pageSize;
26873                 var lastStart = extra ? (total - extra) : total-this.pageSize;
26874                 ds.load({params:{start: lastStart, limit: this.pageSize}});
26875             break;
26876             case "refresh":
26877                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
26878             break;
26879         }
26880     },
26881
26882     /**
26883      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
26884      * @param {Roo.data.Store} store The data store to unbind
26885      */
26886     unbind : function(ds){
26887         ds.un("beforeload", this.beforeLoad, this);
26888         ds.un("load", this.onLoad, this);
26889         ds.un("loadexception", this.onLoadError, this);
26890         ds.un("remove", this.updateInfo, this);
26891         ds.un("add", this.updateInfo, this);
26892         this.ds = undefined;
26893     },
26894
26895     /**
26896      * Binds the paging toolbar to the specified {@link Roo.data.Store}
26897      * @param {Roo.data.Store} store The data store to bind
26898      */
26899     bind : function(ds){
26900         ds.on("beforeload", this.beforeLoad, this);
26901         ds.on("load", this.onLoad, this);
26902         ds.on("loadexception", this.onLoadError, this);
26903         ds.on("remove", this.updateInfo, this);
26904         ds.on("add", this.updateInfo, this);
26905         this.ds = ds;
26906     }
26907 });/*
26908  * - LGPL
26909  *
26910  * element
26911  * 
26912  */
26913
26914 /**
26915  * @class Roo.bootstrap.MessageBar
26916  * @extends Roo.bootstrap.Component
26917  * Bootstrap MessageBar class
26918  * @cfg {String} html contents of the MessageBar
26919  * @cfg {String} weight (info | success | warning | danger) default info
26920  * @cfg {String} beforeClass insert the bar before the given class
26921  * @cfg {Boolean} closable (true | false) default false
26922  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
26923  * 
26924  * @constructor
26925  * Create a new Element
26926  * @param {Object} config The config object
26927  */
26928
26929 Roo.bootstrap.MessageBar = function(config){
26930     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
26931 };
26932
26933 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
26934     
26935     html: '',
26936     weight: 'info',
26937     closable: false,
26938     fixed: false,
26939     beforeClass: 'bootstrap-sticky-wrap',
26940     
26941     getAutoCreate : function(){
26942         
26943         var cfg = {
26944             tag: 'div',
26945             cls: 'alert alert-dismissable alert-' + this.weight,
26946             cn: [
26947                 {
26948                     tag: 'span',
26949                     cls: 'message',
26950                     html: this.html || ''
26951                 }
26952             ]
26953         };
26954         
26955         if(this.fixed){
26956             cfg.cls += ' alert-messages-fixed';
26957         }
26958         
26959         if(this.closable){
26960             cfg.cn.push({
26961                 tag: 'button',
26962                 cls: 'close',
26963                 html: 'x'
26964             });
26965         }
26966         
26967         return cfg;
26968     },
26969     
26970     onRender : function(ct, position)
26971     {
26972         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
26973         
26974         if(!this.el){
26975             var cfg = Roo.apply({},  this.getAutoCreate());
26976             cfg.id = Roo.id();
26977             
26978             if (this.cls) {
26979                 cfg.cls += ' ' + this.cls;
26980             }
26981             if (this.style) {
26982                 cfg.style = this.style;
26983             }
26984             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
26985             
26986             this.el.setVisibilityMode(Roo.Element.DISPLAY);
26987         }
26988         
26989         this.el.select('>button.close').on('click', this.hide, this);
26990         
26991     },
26992     
26993     show : function()
26994     {
26995         if (!this.rendered) {
26996             this.render();
26997         }
26998         
26999         this.el.show();
27000         
27001         this.fireEvent('show', this);
27002         
27003     },
27004     
27005     hide : function()
27006     {
27007         if (!this.rendered) {
27008             this.render();
27009         }
27010         
27011         this.el.hide();
27012         
27013         this.fireEvent('hide', this);
27014     },
27015     
27016     update : function()
27017     {
27018 //        var e = this.el.dom.firstChild;
27019 //        
27020 //        if(this.closable){
27021 //            e = e.nextSibling;
27022 //        }
27023 //        
27024 //        e.data = this.html || '';
27025
27026         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27027     }
27028    
27029 });
27030
27031  
27032
27033      /*
27034  * - LGPL
27035  *
27036  * Graph
27037  * 
27038  */
27039
27040
27041 /**
27042  * @class Roo.bootstrap.Graph
27043  * @extends Roo.bootstrap.Component
27044  * Bootstrap Graph class
27045 > Prameters
27046  -sm {number} sm 4
27047  -md {number} md 5
27048  @cfg {String} graphtype  bar | vbar | pie
27049  @cfg {number} g_x coodinator | centre x (pie)
27050  @cfg {number} g_y coodinator | centre y (pie)
27051  @cfg {number} g_r radius (pie)
27052  @cfg {number} g_height height of the chart (respected by all elements in the set)
27053  @cfg {number} g_width width of the chart (respected by all elements in the set)
27054  @cfg {Object} title The title of the chart
27055     
27056  -{Array}  values
27057  -opts (object) options for the chart 
27058      o {
27059      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27060      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27061      o vgutter (number)
27062      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.
27063      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27064      o to
27065      o stretch (boolean)
27066      o }
27067  -opts (object) options for the pie
27068      o{
27069      o cut
27070      o startAngle (number)
27071      o endAngle (number)
27072      } 
27073  *
27074  * @constructor
27075  * Create a new Input
27076  * @param {Object} config The config object
27077  */
27078
27079 Roo.bootstrap.Graph = function(config){
27080     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27081     
27082     this.addEvents({
27083         // img events
27084         /**
27085          * @event click
27086          * The img click event for the img.
27087          * @param {Roo.EventObject} e
27088          */
27089         "click" : true
27090     });
27091 };
27092
27093 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27094     
27095     sm: 4,
27096     md: 5,
27097     graphtype: 'bar',
27098     g_height: 250,
27099     g_width: 400,
27100     g_x: 50,
27101     g_y: 50,
27102     g_r: 30,
27103     opts:{
27104         //g_colors: this.colors,
27105         g_type: 'soft',
27106         g_gutter: '20%'
27107
27108     },
27109     title : false,
27110
27111     getAutoCreate : function(){
27112         
27113         var cfg = {
27114             tag: 'div',
27115             html : null
27116         };
27117         
27118         
27119         return  cfg;
27120     },
27121
27122     onRender : function(ct,position){
27123         
27124         
27125         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27126         
27127         if (typeof(Raphael) == 'undefined') {
27128             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27129             return;
27130         }
27131         
27132         this.raphael = Raphael(this.el.dom);
27133         
27134                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27135                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27136                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27137                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27138                 /*
27139                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27140                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27141                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27142                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27143                 
27144                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27145                 r.barchart(330, 10, 300, 220, data1);
27146                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27147                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27148                 */
27149                 
27150                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27151                 // r.barchart(30, 30, 560, 250,  xdata, {
27152                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27153                 //     axis : "0 0 1 1",
27154                 //     axisxlabels :  xdata
27155                 //     //yvalues : cols,
27156                    
27157                 // });
27158 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27159 //        
27160 //        this.load(null,xdata,{
27161 //                axis : "0 0 1 1",
27162 //                axisxlabels :  xdata
27163 //                });
27164
27165     },
27166
27167     load : function(graphtype,xdata,opts)
27168     {
27169         this.raphael.clear();
27170         if(!graphtype) {
27171             graphtype = this.graphtype;
27172         }
27173         if(!opts){
27174             opts = this.opts;
27175         }
27176         var r = this.raphael,
27177             fin = function () {
27178                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27179             },
27180             fout = function () {
27181                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27182             },
27183             pfin = function() {
27184                 this.sector.stop();
27185                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27186
27187                 if (this.label) {
27188                     this.label[0].stop();
27189                     this.label[0].attr({ r: 7.5 });
27190                     this.label[1].attr({ "font-weight": 800 });
27191                 }
27192             },
27193             pfout = function() {
27194                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27195
27196                 if (this.label) {
27197                     this.label[0].animate({ r: 5 }, 500, "bounce");
27198                     this.label[1].attr({ "font-weight": 400 });
27199                 }
27200             };
27201
27202         switch(graphtype){
27203             case 'bar':
27204                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27205                 break;
27206             case 'hbar':
27207                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27208                 break;
27209             case 'pie':
27210 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27211 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27212 //            
27213                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27214                 
27215                 break;
27216
27217         }
27218         
27219         if(this.title){
27220             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27221         }
27222         
27223     },
27224     
27225     setTitle: function(o)
27226     {
27227         this.title = o;
27228     },
27229     
27230     initEvents: function() {
27231         
27232         if(!this.href){
27233             this.el.on('click', this.onClick, this);
27234         }
27235     },
27236     
27237     onClick : function(e)
27238     {
27239         Roo.log('img onclick');
27240         this.fireEvent('click', this, e);
27241     }
27242    
27243 });
27244
27245  
27246 /*
27247  * - LGPL
27248  *
27249  * numberBox
27250  * 
27251  */
27252 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27253
27254 /**
27255  * @class Roo.bootstrap.dash.NumberBox
27256  * @extends Roo.bootstrap.Component
27257  * Bootstrap NumberBox class
27258  * @cfg {String} headline Box headline
27259  * @cfg {String} content Box content
27260  * @cfg {String} icon Box icon
27261  * @cfg {String} footer Footer text
27262  * @cfg {String} fhref Footer href
27263  * 
27264  * @constructor
27265  * Create a new NumberBox
27266  * @param {Object} config The config object
27267  */
27268
27269
27270 Roo.bootstrap.dash.NumberBox = function(config){
27271     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27272     
27273 };
27274
27275 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27276     
27277     headline : '',
27278     content : '',
27279     icon : '',
27280     footer : '',
27281     fhref : '',
27282     ficon : '',
27283     
27284     getAutoCreate : function(){
27285         
27286         var cfg = {
27287             tag : 'div',
27288             cls : 'small-box ',
27289             cn : [
27290                 {
27291                     tag : 'div',
27292                     cls : 'inner',
27293                     cn :[
27294                         {
27295                             tag : 'h3',
27296                             cls : 'roo-headline',
27297                             html : this.headline
27298                         },
27299                         {
27300                             tag : 'p',
27301                             cls : 'roo-content',
27302                             html : this.content
27303                         }
27304                     ]
27305                 }
27306             ]
27307         };
27308         
27309         if(this.icon){
27310             cfg.cn.push({
27311                 tag : 'div',
27312                 cls : 'icon',
27313                 cn :[
27314                     {
27315                         tag : 'i',
27316                         cls : 'ion ' + this.icon
27317                     }
27318                 ]
27319             });
27320         }
27321         
27322         if(this.footer){
27323             var footer = {
27324                 tag : 'a',
27325                 cls : 'small-box-footer',
27326                 href : this.fhref || '#',
27327                 html : this.footer
27328             };
27329             
27330             cfg.cn.push(footer);
27331             
27332         }
27333         
27334         return  cfg;
27335     },
27336
27337     onRender : function(ct,position){
27338         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27339
27340
27341        
27342                 
27343     },
27344
27345     setHeadline: function (value)
27346     {
27347         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27348     },
27349     
27350     setFooter: function (value, href)
27351     {
27352         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27353         
27354         if(href){
27355             this.el.select('a.small-box-footer',true).first().attr('href', href);
27356         }
27357         
27358     },
27359
27360     setContent: function (value)
27361     {
27362         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27363     },
27364
27365     initEvents: function() 
27366     {   
27367         
27368     }
27369     
27370 });
27371
27372  
27373 /*
27374  * - LGPL
27375  *
27376  * TabBox
27377  * 
27378  */
27379 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27380
27381 /**
27382  * @class Roo.bootstrap.dash.TabBox
27383  * @extends Roo.bootstrap.Component
27384  * Bootstrap TabBox class
27385  * @cfg {String} title Title of the TabBox
27386  * @cfg {String} icon Icon of the TabBox
27387  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27388  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27389  * 
27390  * @constructor
27391  * Create a new TabBox
27392  * @param {Object} config The config object
27393  */
27394
27395
27396 Roo.bootstrap.dash.TabBox = function(config){
27397     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27398     this.addEvents({
27399         // raw events
27400         /**
27401          * @event addpane
27402          * When a pane is added
27403          * @param {Roo.bootstrap.dash.TabPane} pane
27404          */
27405         "addpane" : true,
27406         /**
27407          * @event activatepane
27408          * When a pane is activated
27409          * @param {Roo.bootstrap.dash.TabPane} pane
27410          */
27411         "activatepane" : true
27412         
27413          
27414     });
27415     
27416     this.panes = [];
27417 };
27418
27419 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27420
27421     title : '',
27422     icon : false,
27423     showtabs : true,
27424     tabScrollable : false,
27425     
27426     getChildContainer : function()
27427     {
27428         return this.el.select('.tab-content', true).first();
27429     },
27430     
27431     getAutoCreate : function(){
27432         
27433         var header = {
27434             tag: 'li',
27435             cls: 'pull-left header',
27436             html: this.title,
27437             cn : []
27438         };
27439         
27440         if(this.icon){
27441             header.cn.push({
27442                 tag: 'i',
27443                 cls: 'fa ' + this.icon
27444             });
27445         }
27446         
27447         var h = {
27448             tag: 'ul',
27449             cls: 'nav nav-tabs pull-right',
27450             cn: [
27451                 header
27452             ]
27453         };
27454         
27455         if(this.tabScrollable){
27456             h = {
27457                 tag: 'div',
27458                 cls: 'tab-header',
27459                 cn: [
27460                     {
27461                         tag: 'ul',
27462                         cls: 'nav nav-tabs pull-right',
27463                         cn: [
27464                             header
27465                         ]
27466                     }
27467                 ]
27468             };
27469         }
27470         
27471         var cfg = {
27472             tag: 'div',
27473             cls: 'nav-tabs-custom',
27474             cn: [
27475                 h,
27476                 {
27477                     tag: 'div',
27478                     cls: 'tab-content no-padding',
27479                     cn: []
27480                 }
27481             ]
27482         };
27483
27484         return  cfg;
27485     },
27486     initEvents : function()
27487     {
27488         //Roo.log('add add pane handler');
27489         this.on('addpane', this.onAddPane, this);
27490     },
27491      /**
27492      * Updates the box title
27493      * @param {String} html to set the title to.
27494      */
27495     setTitle : function(value)
27496     {
27497         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27498     },
27499     onAddPane : function(pane)
27500     {
27501         this.panes.push(pane);
27502         //Roo.log('addpane');
27503         //Roo.log(pane);
27504         // tabs are rendere left to right..
27505         if(!this.showtabs){
27506             return;
27507         }
27508         
27509         var ctr = this.el.select('.nav-tabs', true).first();
27510          
27511          
27512         var existing = ctr.select('.nav-tab',true);
27513         var qty = existing.getCount();;
27514         
27515         
27516         var tab = ctr.createChild({
27517             tag : 'li',
27518             cls : 'nav-tab' + (qty ? '' : ' active'),
27519             cn : [
27520                 {
27521                     tag : 'a',
27522                     href:'#',
27523                     html : pane.title
27524                 }
27525             ]
27526         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27527         pane.tab = tab;
27528         
27529         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27530         if (!qty) {
27531             pane.el.addClass('active');
27532         }
27533         
27534                 
27535     },
27536     onTabClick : function(ev,un,ob,pane)
27537     {
27538         //Roo.log('tab - prev default');
27539         ev.preventDefault();
27540         
27541         
27542         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27543         pane.tab.addClass('active');
27544         //Roo.log(pane.title);
27545         this.getChildContainer().select('.tab-pane',true).removeClass('active');
27546         // technically we should have a deactivate event.. but maybe add later.
27547         // and it should not de-activate the selected tab...
27548         this.fireEvent('activatepane', pane);
27549         pane.el.addClass('active');
27550         pane.fireEvent('activate');
27551         
27552         
27553     },
27554     
27555     getActivePane : function()
27556     {
27557         var r = false;
27558         Roo.each(this.panes, function(p) {
27559             if(p.el.hasClass('active')){
27560                 r = p;
27561                 return false;
27562             }
27563             
27564             return;
27565         });
27566         
27567         return r;
27568     }
27569     
27570     
27571 });
27572
27573  
27574 /*
27575  * - LGPL
27576  *
27577  * Tab pane
27578  * 
27579  */
27580 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27581 /**
27582  * @class Roo.bootstrap.TabPane
27583  * @extends Roo.bootstrap.Component
27584  * Bootstrap TabPane class
27585  * @cfg {Boolean} active (false | true) Default false
27586  * @cfg {String} title title of panel
27587
27588  * 
27589  * @constructor
27590  * Create a new TabPane
27591  * @param {Object} config The config object
27592  */
27593
27594 Roo.bootstrap.dash.TabPane = function(config){
27595     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27596     
27597     this.addEvents({
27598         // raw events
27599         /**
27600          * @event activate
27601          * When a pane is activated
27602          * @param {Roo.bootstrap.dash.TabPane} pane
27603          */
27604         "activate" : true
27605          
27606     });
27607 };
27608
27609 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
27610     
27611     active : false,
27612     title : '',
27613     
27614     // the tabBox that this is attached to.
27615     tab : false,
27616      
27617     getAutoCreate : function() 
27618     {
27619         var cfg = {
27620             tag: 'div',
27621             cls: 'tab-pane'
27622         };
27623         
27624         if(this.active){
27625             cfg.cls += ' active';
27626         }
27627         
27628         return cfg;
27629     },
27630     initEvents  : function()
27631     {
27632         //Roo.log('trigger add pane handler');
27633         this.parent().fireEvent('addpane', this)
27634     },
27635     
27636      /**
27637      * Updates the tab title 
27638      * @param {String} html to set the title to.
27639      */
27640     setTitle: function(str)
27641     {
27642         if (!this.tab) {
27643             return;
27644         }
27645         this.title = str;
27646         this.tab.select('a', true).first().dom.innerHTML = str;
27647         
27648     }
27649     
27650     
27651     
27652 });
27653
27654  
27655
27656
27657  /*
27658  * - LGPL
27659  *
27660  * menu
27661  * 
27662  */
27663 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27664
27665 /**
27666  * @class Roo.bootstrap.menu.Menu
27667  * @extends Roo.bootstrap.Component
27668  * Bootstrap Menu class - container for Menu
27669  * @cfg {String} html Text of the menu
27670  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27671  * @cfg {String} icon Font awesome icon
27672  * @cfg {String} pos Menu align to (top | bottom) default bottom
27673  * 
27674  * 
27675  * @constructor
27676  * Create a new Menu
27677  * @param {Object} config The config object
27678  */
27679
27680
27681 Roo.bootstrap.menu.Menu = function(config){
27682     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27683     
27684     this.addEvents({
27685         /**
27686          * @event beforeshow
27687          * Fires before this menu is displayed
27688          * @param {Roo.bootstrap.menu.Menu} this
27689          */
27690         beforeshow : true,
27691         /**
27692          * @event beforehide
27693          * Fires before this menu is hidden
27694          * @param {Roo.bootstrap.menu.Menu} this
27695          */
27696         beforehide : true,
27697         /**
27698          * @event show
27699          * Fires after this menu is displayed
27700          * @param {Roo.bootstrap.menu.Menu} this
27701          */
27702         show : true,
27703         /**
27704          * @event hide
27705          * Fires after this menu is hidden
27706          * @param {Roo.bootstrap.menu.Menu} this
27707          */
27708         hide : true,
27709         /**
27710          * @event click
27711          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27712          * @param {Roo.bootstrap.menu.Menu} this
27713          * @param {Roo.EventObject} e
27714          */
27715         click : true
27716     });
27717     
27718 };
27719
27720 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
27721     
27722     submenu : false,
27723     html : '',
27724     weight : 'default',
27725     icon : false,
27726     pos : 'bottom',
27727     
27728     
27729     getChildContainer : function() {
27730         if(this.isSubMenu){
27731             return this.el;
27732         }
27733         
27734         return this.el.select('ul.dropdown-menu', true).first();  
27735     },
27736     
27737     getAutoCreate : function()
27738     {
27739         var text = [
27740             {
27741                 tag : 'span',
27742                 cls : 'roo-menu-text',
27743                 html : this.html
27744             }
27745         ];
27746         
27747         if(this.icon){
27748             text.unshift({
27749                 tag : 'i',
27750                 cls : 'fa ' + this.icon
27751             })
27752         }
27753         
27754         
27755         var cfg = {
27756             tag : 'div',
27757             cls : 'btn-group',
27758             cn : [
27759                 {
27760                     tag : 'button',
27761                     cls : 'dropdown-button btn btn-' + this.weight,
27762                     cn : text
27763                 },
27764                 {
27765                     tag : 'button',
27766                     cls : 'dropdown-toggle btn btn-' + this.weight,
27767                     cn : [
27768                         {
27769                             tag : 'span',
27770                             cls : 'caret'
27771                         }
27772                     ]
27773                 },
27774                 {
27775                     tag : 'ul',
27776                     cls : 'dropdown-menu'
27777                 }
27778             ]
27779             
27780         };
27781         
27782         if(this.pos == 'top'){
27783             cfg.cls += ' dropup';
27784         }
27785         
27786         if(this.isSubMenu){
27787             cfg = {
27788                 tag : 'ul',
27789                 cls : 'dropdown-menu'
27790             }
27791         }
27792         
27793         return cfg;
27794     },
27795     
27796     onRender : function(ct, position)
27797     {
27798         this.isSubMenu = ct.hasClass('dropdown-submenu');
27799         
27800         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
27801     },
27802     
27803     initEvents : function() 
27804     {
27805         if(this.isSubMenu){
27806             return;
27807         }
27808         
27809         this.hidden = true;
27810         
27811         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
27812         this.triggerEl.on('click', this.onTriggerPress, this);
27813         
27814         this.buttonEl = this.el.select('button.dropdown-button', true).first();
27815         this.buttonEl.on('click', this.onClick, this);
27816         
27817     },
27818     
27819     list : function()
27820     {
27821         if(this.isSubMenu){
27822             return this.el;
27823         }
27824         
27825         return this.el.select('ul.dropdown-menu', true).first();
27826     },
27827     
27828     onClick : function(e)
27829     {
27830         this.fireEvent("click", this, e);
27831     },
27832     
27833     onTriggerPress  : function(e)
27834     {   
27835         if (this.isVisible()) {
27836             this.hide();
27837         } else {
27838             this.show();
27839         }
27840     },
27841     
27842     isVisible : function(){
27843         return !this.hidden;
27844     },
27845     
27846     show : function()
27847     {
27848         this.fireEvent("beforeshow", this);
27849         
27850         this.hidden = false;
27851         this.el.addClass('open');
27852         
27853         Roo.get(document).on("mouseup", this.onMouseUp, this);
27854         
27855         this.fireEvent("show", this);
27856         
27857         
27858     },
27859     
27860     hide : function()
27861     {
27862         this.fireEvent("beforehide", this);
27863         
27864         this.hidden = true;
27865         this.el.removeClass('open');
27866         
27867         Roo.get(document).un("mouseup", this.onMouseUp);
27868         
27869         this.fireEvent("hide", this);
27870     },
27871     
27872     onMouseUp : function()
27873     {
27874         this.hide();
27875     }
27876     
27877 });
27878
27879  
27880  /*
27881  * - LGPL
27882  *
27883  * menu item
27884  * 
27885  */
27886 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27887
27888 /**
27889  * @class Roo.bootstrap.menu.Item
27890  * @extends Roo.bootstrap.Component
27891  * Bootstrap MenuItem class
27892  * @cfg {Boolean} submenu (true | false) default false
27893  * @cfg {String} html text of the item
27894  * @cfg {String} href the link
27895  * @cfg {Boolean} disable (true | false) default false
27896  * @cfg {Boolean} preventDefault (true | false) default true
27897  * @cfg {String} icon Font awesome icon
27898  * @cfg {String} pos Submenu align to (left | right) default right 
27899  * 
27900  * 
27901  * @constructor
27902  * Create a new Item
27903  * @param {Object} config The config object
27904  */
27905
27906
27907 Roo.bootstrap.menu.Item = function(config){
27908     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
27909     this.addEvents({
27910         /**
27911          * @event mouseover
27912          * Fires when the mouse is hovering over this menu
27913          * @param {Roo.bootstrap.menu.Item} this
27914          * @param {Roo.EventObject} e
27915          */
27916         mouseover : true,
27917         /**
27918          * @event mouseout
27919          * Fires when the mouse exits this menu
27920          * @param {Roo.bootstrap.menu.Item} this
27921          * @param {Roo.EventObject} e
27922          */
27923         mouseout : true,
27924         // raw events
27925         /**
27926          * @event click
27927          * The raw click event for the entire grid.
27928          * @param {Roo.EventObject} e
27929          */
27930         click : true
27931     });
27932 };
27933
27934 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
27935     
27936     submenu : false,
27937     href : '',
27938     html : '',
27939     preventDefault: true,
27940     disable : false,
27941     icon : false,
27942     pos : 'right',
27943     
27944     getAutoCreate : function()
27945     {
27946         var text = [
27947             {
27948                 tag : 'span',
27949                 cls : 'roo-menu-item-text',
27950                 html : this.html
27951             }
27952         ];
27953         
27954         if(this.icon){
27955             text.unshift({
27956                 tag : 'i',
27957                 cls : 'fa ' + this.icon
27958             })
27959         }
27960         
27961         var cfg = {
27962             tag : 'li',
27963             cn : [
27964                 {
27965                     tag : 'a',
27966                     href : this.href || '#',
27967                     cn : text
27968                 }
27969             ]
27970         };
27971         
27972         if(this.disable){
27973             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
27974         }
27975         
27976         if(this.submenu){
27977             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
27978             
27979             if(this.pos == 'left'){
27980                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
27981             }
27982         }
27983         
27984         return cfg;
27985     },
27986     
27987     initEvents : function() 
27988     {
27989         this.el.on('mouseover', this.onMouseOver, this);
27990         this.el.on('mouseout', this.onMouseOut, this);
27991         
27992         this.el.select('a', true).first().on('click', this.onClick, this);
27993         
27994     },
27995     
27996     onClick : function(e)
27997     {
27998         if(this.preventDefault){
27999             e.preventDefault();
28000         }
28001         
28002         this.fireEvent("click", this, e);
28003     },
28004     
28005     onMouseOver : function(e)
28006     {
28007         if(this.submenu && this.pos == 'left'){
28008             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28009         }
28010         
28011         this.fireEvent("mouseover", this, e);
28012     },
28013     
28014     onMouseOut : function(e)
28015     {
28016         this.fireEvent("mouseout", this, e);
28017     }
28018 });
28019
28020  
28021
28022  /*
28023  * - LGPL
28024  *
28025  * menu separator
28026  * 
28027  */
28028 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28029
28030 /**
28031  * @class Roo.bootstrap.menu.Separator
28032  * @extends Roo.bootstrap.Component
28033  * Bootstrap Separator class
28034  * 
28035  * @constructor
28036  * Create a new Separator
28037  * @param {Object} config The config object
28038  */
28039
28040
28041 Roo.bootstrap.menu.Separator = function(config){
28042     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28043 };
28044
28045 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28046     
28047     getAutoCreate : function(){
28048         var cfg = {
28049             tag : 'li',
28050             cls: 'divider'
28051         };
28052         
28053         return cfg;
28054     }
28055    
28056 });
28057
28058  
28059
28060  /*
28061  * - LGPL
28062  *
28063  * Tooltip
28064  * 
28065  */
28066
28067 /**
28068  * @class Roo.bootstrap.Tooltip
28069  * Bootstrap Tooltip class
28070  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28071  * to determine which dom element triggers the tooltip.
28072  * 
28073  * It needs to add support for additional attributes like tooltip-position
28074  * 
28075  * @constructor
28076  * Create a new Toolti
28077  * @param {Object} config The config object
28078  */
28079
28080 Roo.bootstrap.Tooltip = function(config){
28081     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28082     
28083     this.alignment = Roo.bootstrap.Tooltip.alignment;
28084     
28085     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28086         this.alignment = config.alignment;
28087     }
28088     
28089 };
28090
28091 Roo.apply(Roo.bootstrap.Tooltip, {
28092     /**
28093      * @function init initialize tooltip monitoring.
28094      * @static
28095      */
28096     currentEl : false,
28097     currentTip : false,
28098     currentRegion : false,
28099     
28100     //  init : delay?
28101     
28102     init : function()
28103     {
28104         Roo.get(document).on('mouseover', this.enter ,this);
28105         Roo.get(document).on('mouseout', this.leave, this);
28106          
28107         
28108         this.currentTip = new Roo.bootstrap.Tooltip();
28109     },
28110     
28111     enter : function(ev)
28112     {
28113         var dom = ev.getTarget();
28114         
28115         //Roo.log(['enter',dom]);
28116         var el = Roo.fly(dom);
28117         if (this.currentEl) {
28118             //Roo.log(dom);
28119             //Roo.log(this.currentEl);
28120             //Roo.log(this.currentEl.contains(dom));
28121             if (this.currentEl == el) {
28122                 return;
28123             }
28124             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28125                 return;
28126             }
28127
28128         }
28129         
28130         if (this.currentTip.el) {
28131             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28132         }    
28133         //Roo.log(ev);
28134         
28135         if(!el || el.dom == document){
28136             return;
28137         }
28138         
28139         var bindEl = el;
28140         
28141         // you can not look for children, as if el is the body.. then everythign is the child..
28142         if (!el.attr('tooltip')) { //
28143             if (!el.select("[tooltip]").elements.length) {
28144                 return;
28145             }
28146             // is the mouse over this child...?
28147             bindEl = el.select("[tooltip]").first();
28148             var xy = ev.getXY();
28149             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28150                 //Roo.log("not in region.");
28151                 return;
28152             }
28153             //Roo.log("child element over..");
28154             
28155         }
28156         this.currentEl = bindEl;
28157         this.currentTip.bind(bindEl);
28158         this.currentRegion = Roo.lib.Region.getRegion(dom);
28159         this.currentTip.enter();
28160         
28161     },
28162     leave : function(ev)
28163     {
28164         var dom = ev.getTarget();
28165         //Roo.log(['leave',dom]);
28166         if (!this.currentEl) {
28167             return;
28168         }
28169         
28170         
28171         if (dom != this.currentEl.dom) {
28172             return;
28173         }
28174         var xy = ev.getXY();
28175         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28176             return;
28177         }
28178         // only activate leave if mouse cursor is outside... bounding box..
28179         
28180         
28181         
28182         
28183         if (this.currentTip) {
28184             this.currentTip.leave();
28185         }
28186         //Roo.log('clear currentEl');
28187         this.currentEl = false;
28188         
28189         
28190     },
28191     alignment : {
28192         'left' : ['r-l', [-2,0], 'right'],
28193         'right' : ['l-r', [2,0], 'left'],
28194         'bottom' : ['t-b', [0,2], 'top'],
28195         'top' : [ 'b-t', [0,-2], 'bottom']
28196     }
28197     
28198 });
28199
28200
28201 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28202     
28203     
28204     bindEl : false,
28205     
28206     delay : null, // can be { show : 300 , hide: 500}
28207     
28208     timeout : null,
28209     
28210     hoverState : null, //???
28211     
28212     placement : 'bottom', 
28213     
28214     alignment : false,
28215     
28216     getAutoCreate : function(){
28217     
28218         var cfg = {
28219            cls : 'tooltip',
28220            role : 'tooltip',
28221            cn : [
28222                 {
28223                     cls : 'tooltip-arrow'
28224                 },
28225                 {
28226                     cls : 'tooltip-inner'
28227                 }
28228            ]
28229         };
28230         
28231         return cfg;
28232     },
28233     bind : function(el)
28234     {
28235         this.bindEl = el;
28236     },
28237       
28238     
28239     enter : function () {
28240        
28241         if (this.timeout != null) {
28242             clearTimeout(this.timeout);
28243         }
28244         
28245         this.hoverState = 'in';
28246          //Roo.log("enter - show");
28247         if (!this.delay || !this.delay.show) {
28248             this.show();
28249             return;
28250         }
28251         var _t = this;
28252         this.timeout = setTimeout(function () {
28253             if (_t.hoverState == 'in') {
28254                 _t.show();
28255             }
28256         }, this.delay.show);
28257     },
28258     leave : function()
28259     {
28260         clearTimeout(this.timeout);
28261     
28262         this.hoverState = 'out';
28263          if (!this.delay || !this.delay.hide) {
28264             this.hide();
28265             return;
28266         }
28267        
28268         var _t = this;
28269         this.timeout = setTimeout(function () {
28270             //Roo.log("leave - timeout");
28271             
28272             if (_t.hoverState == 'out') {
28273                 _t.hide();
28274                 Roo.bootstrap.Tooltip.currentEl = false;
28275             }
28276         }, delay);
28277     },
28278     
28279     show : function (msg)
28280     {
28281         if (!this.el) {
28282             this.render(document.body);
28283         }
28284         // set content.
28285         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28286         
28287         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28288         
28289         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28290         
28291         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
28292         
28293         var placement = typeof this.placement == 'function' ?
28294             this.placement.call(this, this.el, on_el) :
28295             this.placement;
28296             
28297         var autoToken = /\s?auto?\s?/i;
28298         var autoPlace = autoToken.test(placement);
28299         if (autoPlace) {
28300             placement = placement.replace(autoToken, '') || 'top';
28301         }
28302         
28303         //this.el.detach()
28304         //this.el.setXY([0,0]);
28305         this.el.show();
28306         //this.el.dom.style.display='block';
28307         
28308         //this.el.appendTo(on_el);
28309         
28310         var p = this.getPosition();
28311         var box = this.el.getBox();
28312         
28313         if (autoPlace) {
28314             // fixme..
28315         }
28316         
28317         var align = this.alignment[placement];
28318         
28319         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28320         
28321         if(placement == 'top' || placement == 'bottom'){
28322             if(xy[0] < 0){
28323                 placement = 'right';
28324             }
28325             
28326             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28327                 placement = 'left';
28328             }
28329             
28330             var scroll = Roo.select('body', true).first().getScroll();
28331             
28332             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28333                 placement = 'top';
28334             }
28335             
28336             align = this.alignment[placement];
28337         }
28338         
28339         this.el.alignTo(this.bindEl, align[0],align[1]);
28340         //var arrow = this.el.select('.arrow',true).first();
28341         //arrow.set(align[2], 
28342         
28343         this.el.addClass(placement);
28344         
28345         this.el.addClass('in fade');
28346         
28347         this.hoverState = null;
28348         
28349         if (this.el.hasClass('fade')) {
28350             // fade it?
28351         }
28352         
28353     },
28354     hide : function()
28355     {
28356          
28357         if (!this.el) {
28358             return;
28359         }
28360         //this.el.setXY([0,0]);
28361         this.el.removeClass('in');
28362         //this.el.hide();
28363         
28364     }
28365     
28366 });
28367  
28368
28369  /*
28370  * - LGPL
28371  *
28372  * Location Picker
28373  * 
28374  */
28375
28376 /**
28377  * @class Roo.bootstrap.LocationPicker
28378  * @extends Roo.bootstrap.Component
28379  * Bootstrap LocationPicker class
28380  * @cfg {Number} latitude Position when init default 0
28381  * @cfg {Number} longitude Position when init default 0
28382  * @cfg {Number} zoom default 15
28383  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28384  * @cfg {Boolean} mapTypeControl default false
28385  * @cfg {Boolean} disableDoubleClickZoom default false
28386  * @cfg {Boolean} scrollwheel default true
28387  * @cfg {Boolean} streetViewControl default false
28388  * @cfg {Number} radius default 0
28389  * @cfg {String} locationName
28390  * @cfg {Boolean} draggable default true
28391  * @cfg {Boolean} enableAutocomplete default false
28392  * @cfg {Boolean} enableReverseGeocode default true
28393  * @cfg {String} markerTitle
28394  * 
28395  * @constructor
28396  * Create a new LocationPicker
28397  * @param {Object} config The config object
28398  */
28399
28400
28401 Roo.bootstrap.LocationPicker = function(config){
28402     
28403     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28404     
28405     this.addEvents({
28406         /**
28407          * @event initial
28408          * Fires when the picker initialized.
28409          * @param {Roo.bootstrap.LocationPicker} this
28410          * @param {Google Location} location
28411          */
28412         initial : true,
28413         /**
28414          * @event positionchanged
28415          * Fires when the picker position changed.
28416          * @param {Roo.bootstrap.LocationPicker} this
28417          * @param {Google Location} location
28418          */
28419         positionchanged : true,
28420         /**
28421          * @event resize
28422          * Fires when the map resize.
28423          * @param {Roo.bootstrap.LocationPicker} this
28424          */
28425         resize : true,
28426         /**
28427          * @event show
28428          * Fires when the map show.
28429          * @param {Roo.bootstrap.LocationPicker} this
28430          */
28431         show : true,
28432         /**
28433          * @event hide
28434          * Fires when the map hide.
28435          * @param {Roo.bootstrap.LocationPicker} this
28436          */
28437         hide : true,
28438         /**
28439          * @event mapClick
28440          * Fires when click the map.
28441          * @param {Roo.bootstrap.LocationPicker} this
28442          * @param {Map event} e
28443          */
28444         mapClick : true,
28445         /**
28446          * @event mapRightClick
28447          * Fires when right click the map.
28448          * @param {Roo.bootstrap.LocationPicker} this
28449          * @param {Map event} e
28450          */
28451         mapRightClick : true,
28452         /**
28453          * @event markerClick
28454          * Fires when click the marker.
28455          * @param {Roo.bootstrap.LocationPicker} this
28456          * @param {Map event} e
28457          */
28458         markerClick : true,
28459         /**
28460          * @event markerRightClick
28461          * Fires when right click the marker.
28462          * @param {Roo.bootstrap.LocationPicker} this
28463          * @param {Map event} e
28464          */
28465         markerRightClick : true,
28466         /**
28467          * @event OverlayViewDraw
28468          * Fires when OverlayView Draw
28469          * @param {Roo.bootstrap.LocationPicker} this
28470          */
28471         OverlayViewDraw : true,
28472         /**
28473          * @event OverlayViewOnAdd
28474          * Fires when OverlayView Draw
28475          * @param {Roo.bootstrap.LocationPicker} this
28476          */
28477         OverlayViewOnAdd : true,
28478         /**
28479          * @event OverlayViewOnRemove
28480          * Fires when OverlayView Draw
28481          * @param {Roo.bootstrap.LocationPicker} this
28482          */
28483         OverlayViewOnRemove : true,
28484         /**
28485          * @event OverlayViewShow
28486          * Fires when OverlayView Draw
28487          * @param {Roo.bootstrap.LocationPicker} this
28488          * @param {Pixel} cpx
28489          */
28490         OverlayViewShow : true,
28491         /**
28492          * @event OverlayViewHide
28493          * Fires when OverlayView Draw
28494          * @param {Roo.bootstrap.LocationPicker} this
28495          */
28496         OverlayViewHide : true,
28497         /**
28498          * @event loadexception
28499          * Fires when load google lib failed.
28500          * @param {Roo.bootstrap.LocationPicker} this
28501          */
28502         loadexception : true
28503     });
28504         
28505 };
28506
28507 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
28508     
28509     gMapContext: false,
28510     
28511     latitude: 0,
28512     longitude: 0,
28513     zoom: 15,
28514     mapTypeId: false,
28515     mapTypeControl: false,
28516     disableDoubleClickZoom: false,
28517     scrollwheel: true,
28518     streetViewControl: false,
28519     radius: 0,
28520     locationName: '',
28521     draggable: true,
28522     enableAutocomplete: false,
28523     enableReverseGeocode: true,
28524     markerTitle: '',
28525     
28526     getAutoCreate: function()
28527     {
28528
28529         var cfg = {
28530             tag: 'div',
28531             cls: 'roo-location-picker'
28532         };
28533         
28534         return cfg
28535     },
28536     
28537     initEvents: function(ct, position)
28538     {       
28539         if(!this.el.getWidth() || this.isApplied()){
28540             return;
28541         }
28542         
28543         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28544         
28545         this.initial();
28546     },
28547     
28548     initial: function()
28549     {
28550         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28551             this.fireEvent('loadexception', this);
28552             return;
28553         }
28554         
28555         if(!this.mapTypeId){
28556             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28557         }
28558         
28559         this.gMapContext = this.GMapContext();
28560         
28561         this.initOverlayView();
28562         
28563         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28564         
28565         var _this = this;
28566                 
28567         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28568             _this.setPosition(_this.gMapContext.marker.position);
28569         });
28570         
28571         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28572             _this.fireEvent('mapClick', this, event);
28573             
28574         });
28575
28576         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28577             _this.fireEvent('mapRightClick', this, event);
28578             
28579         });
28580         
28581         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28582             _this.fireEvent('markerClick', this, event);
28583             
28584         });
28585
28586         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28587             _this.fireEvent('markerRightClick', this, event);
28588             
28589         });
28590         
28591         this.setPosition(this.gMapContext.location);
28592         
28593         this.fireEvent('initial', this, this.gMapContext.location);
28594     },
28595     
28596     initOverlayView: function()
28597     {
28598         var _this = this;
28599         
28600         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28601             
28602             draw: function()
28603             {
28604                 _this.fireEvent('OverlayViewDraw', _this);
28605             },
28606             
28607             onAdd: function()
28608             {
28609                 _this.fireEvent('OverlayViewOnAdd', _this);
28610             },
28611             
28612             onRemove: function()
28613             {
28614                 _this.fireEvent('OverlayViewOnRemove', _this);
28615             },
28616             
28617             show: function(cpx)
28618             {
28619                 _this.fireEvent('OverlayViewShow', _this, cpx);
28620             },
28621             
28622             hide: function()
28623             {
28624                 _this.fireEvent('OverlayViewHide', _this);
28625             }
28626             
28627         });
28628     },
28629     
28630     fromLatLngToContainerPixel: function(event)
28631     {
28632         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28633     },
28634     
28635     isApplied: function() 
28636     {
28637         return this.getGmapContext() == false ? false : true;
28638     },
28639     
28640     getGmapContext: function() 
28641     {
28642         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28643     },
28644     
28645     GMapContext: function() 
28646     {
28647         var position = new google.maps.LatLng(this.latitude, this.longitude);
28648         
28649         var _map = new google.maps.Map(this.el.dom, {
28650             center: position,
28651             zoom: this.zoom,
28652             mapTypeId: this.mapTypeId,
28653             mapTypeControl: this.mapTypeControl,
28654             disableDoubleClickZoom: this.disableDoubleClickZoom,
28655             scrollwheel: this.scrollwheel,
28656             streetViewControl: this.streetViewControl,
28657             locationName: this.locationName,
28658             draggable: this.draggable,
28659             enableAutocomplete: this.enableAutocomplete,
28660             enableReverseGeocode: this.enableReverseGeocode
28661         });
28662         
28663         var _marker = new google.maps.Marker({
28664             position: position,
28665             map: _map,
28666             title: this.markerTitle,
28667             draggable: this.draggable
28668         });
28669         
28670         return {
28671             map: _map,
28672             marker: _marker,
28673             circle: null,
28674             location: position,
28675             radius: this.radius,
28676             locationName: this.locationName,
28677             addressComponents: {
28678                 formatted_address: null,
28679                 addressLine1: null,
28680                 addressLine2: null,
28681                 streetName: null,
28682                 streetNumber: null,
28683                 city: null,
28684                 district: null,
28685                 state: null,
28686                 stateOrProvince: null
28687             },
28688             settings: this,
28689             domContainer: this.el.dom,
28690             geodecoder: new google.maps.Geocoder()
28691         };
28692     },
28693     
28694     drawCircle: function(center, radius, options) 
28695     {
28696         if (this.gMapContext.circle != null) {
28697             this.gMapContext.circle.setMap(null);
28698         }
28699         if (radius > 0) {
28700             radius *= 1;
28701             options = Roo.apply({}, options, {
28702                 strokeColor: "#0000FF",
28703                 strokeOpacity: .35,
28704                 strokeWeight: 2,
28705                 fillColor: "#0000FF",
28706                 fillOpacity: .2
28707             });
28708             
28709             options.map = this.gMapContext.map;
28710             options.radius = radius;
28711             options.center = center;
28712             this.gMapContext.circle = new google.maps.Circle(options);
28713             return this.gMapContext.circle;
28714         }
28715         
28716         return null;
28717     },
28718     
28719     setPosition: function(location) 
28720     {
28721         this.gMapContext.location = location;
28722         this.gMapContext.marker.setPosition(location);
28723         this.gMapContext.map.panTo(location);
28724         this.drawCircle(location, this.gMapContext.radius, {});
28725         
28726         var _this = this;
28727         
28728         if (this.gMapContext.settings.enableReverseGeocode) {
28729             this.gMapContext.geodecoder.geocode({
28730                 latLng: this.gMapContext.location
28731             }, function(results, status) {
28732                 
28733                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28734                     _this.gMapContext.locationName = results[0].formatted_address;
28735                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28736                     
28737                     _this.fireEvent('positionchanged', this, location);
28738                 }
28739             });
28740             
28741             return;
28742         }
28743         
28744         this.fireEvent('positionchanged', this, location);
28745     },
28746     
28747     resize: function()
28748     {
28749         google.maps.event.trigger(this.gMapContext.map, "resize");
28750         
28751         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28752         
28753         this.fireEvent('resize', this);
28754     },
28755     
28756     setPositionByLatLng: function(latitude, longitude)
28757     {
28758         this.setPosition(new google.maps.LatLng(latitude, longitude));
28759     },
28760     
28761     getCurrentPosition: function() 
28762     {
28763         return {
28764             latitude: this.gMapContext.location.lat(),
28765             longitude: this.gMapContext.location.lng()
28766         };
28767     },
28768     
28769     getAddressName: function() 
28770     {
28771         return this.gMapContext.locationName;
28772     },
28773     
28774     getAddressComponents: function() 
28775     {
28776         return this.gMapContext.addressComponents;
28777     },
28778     
28779     address_component_from_google_geocode: function(address_components) 
28780     {
28781         var result = {};
28782         
28783         for (var i = 0; i < address_components.length; i++) {
28784             var component = address_components[i];
28785             if (component.types.indexOf("postal_code") >= 0) {
28786                 result.postalCode = component.short_name;
28787             } else if (component.types.indexOf("street_number") >= 0) {
28788                 result.streetNumber = component.short_name;
28789             } else if (component.types.indexOf("route") >= 0) {
28790                 result.streetName = component.short_name;
28791             } else if (component.types.indexOf("neighborhood") >= 0) {
28792                 result.city = component.short_name;
28793             } else if (component.types.indexOf("locality") >= 0) {
28794                 result.city = component.short_name;
28795             } else if (component.types.indexOf("sublocality") >= 0) {
28796                 result.district = component.short_name;
28797             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
28798                 result.stateOrProvince = component.short_name;
28799             } else if (component.types.indexOf("country") >= 0) {
28800                 result.country = component.short_name;
28801             }
28802         }
28803         
28804         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
28805         result.addressLine2 = "";
28806         return result;
28807     },
28808     
28809     setZoomLevel: function(zoom)
28810     {
28811         this.gMapContext.map.setZoom(zoom);
28812     },
28813     
28814     show: function()
28815     {
28816         if(!this.el){
28817             return;
28818         }
28819         
28820         this.el.show();
28821         
28822         this.resize();
28823         
28824         this.fireEvent('show', this);
28825     },
28826     
28827     hide: function()
28828     {
28829         if(!this.el){
28830             return;
28831         }
28832         
28833         this.el.hide();
28834         
28835         this.fireEvent('hide', this);
28836     }
28837     
28838 });
28839
28840 Roo.apply(Roo.bootstrap.LocationPicker, {
28841     
28842     OverlayView : function(map, options)
28843     {
28844         options = options || {};
28845         
28846         this.setMap(map);
28847     }
28848     
28849     
28850 });/**
28851  * @class Roo.bootstrap.Alert
28852  * @extends Roo.bootstrap.Component
28853  * Bootstrap Alert class - shows an alert area box
28854  * eg
28855  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
28856   Enter a valid email address
28857 </div>
28858  * @licence LGPL
28859  * @cfg {String} title The title of alert
28860  * @cfg {String} html The content of alert
28861  * @cfg {String} weight (  success | info | warning | danger )
28862  * @cfg {String} faicon font-awesomeicon
28863  * 
28864  * @constructor
28865  * Create a new alert
28866  * @param {Object} config The config object
28867  */
28868
28869
28870 Roo.bootstrap.Alert = function(config){
28871     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
28872     
28873 };
28874
28875 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
28876     
28877     title: '',
28878     html: '',
28879     weight: false,
28880     faicon: false,
28881     
28882     getAutoCreate : function()
28883     {
28884         
28885         var cfg = {
28886             tag : 'div',
28887             cls : 'alert',
28888             cn : [
28889                 {
28890                     tag : 'i',
28891                     cls : 'roo-alert-icon'
28892                     
28893                 },
28894                 {
28895                     tag : 'b',
28896                     cls : 'roo-alert-title',
28897                     html : this.title
28898                 },
28899                 {
28900                     tag : 'span',
28901                     cls : 'roo-alert-text',
28902                     html : this.html
28903                 }
28904             ]
28905         };
28906         
28907         if(this.faicon){
28908             cfg.cn[0].cls += ' fa ' + this.faicon;
28909         }
28910         
28911         if(this.weight){
28912             cfg.cls += ' alert-' + this.weight;
28913         }
28914         
28915         return cfg;
28916     },
28917     
28918     initEvents: function() 
28919     {
28920         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28921     },
28922     
28923     setTitle : function(str)
28924     {
28925         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
28926     },
28927     
28928     setText : function(str)
28929     {
28930         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
28931     },
28932     
28933     setWeight : function(weight)
28934     {
28935         if(this.weight){
28936             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
28937         }
28938         
28939         this.weight = weight;
28940         
28941         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
28942     },
28943     
28944     setIcon : function(icon)
28945     {
28946         if(this.faicon){
28947             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
28948         }
28949         
28950         this.faicon = icon;
28951         
28952         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
28953     },
28954     
28955     hide: function() 
28956     {
28957         this.el.hide();   
28958     },
28959     
28960     show: function() 
28961     {  
28962         this.el.show();   
28963     }
28964     
28965 });
28966
28967  
28968 /*
28969 * Licence: LGPL
28970 */
28971
28972 /**
28973  * @class Roo.bootstrap.UploadCropbox
28974  * @extends Roo.bootstrap.Component
28975  * Bootstrap UploadCropbox class
28976  * @cfg {String} emptyText show when image has been loaded
28977  * @cfg {String} rotateNotify show when image too small to rotate
28978  * @cfg {Number} errorTimeout default 3000
28979  * @cfg {Number} minWidth default 300
28980  * @cfg {Number} minHeight default 300
28981  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
28982  * @cfg {Boolean} isDocument (true|false) default false
28983  * @cfg {String} url action url
28984  * @cfg {String} paramName default 'imageUpload'
28985  * @cfg {String} method default POST
28986  * @cfg {Boolean} loadMask (true|false) default true
28987  * @cfg {Boolean} loadingText default 'Loading...'
28988  * 
28989  * @constructor
28990  * Create a new UploadCropbox
28991  * @param {Object} config The config object
28992  */
28993
28994 Roo.bootstrap.UploadCropbox = function(config){
28995     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
28996     
28997     this.addEvents({
28998         /**
28999          * @event beforeselectfile
29000          * Fire before select file
29001          * @param {Roo.bootstrap.UploadCropbox} this
29002          */
29003         "beforeselectfile" : true,
29004         /**
29005          * @event initial
29006          * Fire after initEvent
29007          * @param {Roo.bootstrap.UploadCropbox} this
29008          */
29009         "initial" : true,
29010         /**
29011          * @event crop
29012          * Fire after initEvent
29013          * @param {Roo.bootstrap.UploadCropbox} this
29014          * @param {String} data
29015          */
29016         "crop" : true,
29017         /**
29018          * @event prepare
29019          * Fire when preparing the file data
29020          * @param {Roo.bootstrap.UploadCropbox} this
29021          * @param {Object} file
29022          */
29023         "prepare" : true,
29024         /**
29025          * @event exception
29026          * Fire when get exception
29027          * @param {Roo.bootstrap.UploadCropbox} this
29028          * @param {XMLHttpRequest} xhr
29029          */
29030         "exception" : true,
29031         /**
29032          * @event beforeloadcanvas
29033          * Fire before load the canvas
29034          * @param {Roo.bootstrap.UploadCropbox} this
29035          * @param {String} src
29036          */
29037         "beforeloadcanvas" : true,
29038         /**
29039          * @event trash
29040          * Fire when trash image
29041          * @param {Roo.bootstrap.UploadCropbox} this
29042          */
29043         "trash" : true,
29044         /**
29045          * @event download
29046          * Fire when download the image
29047          * @param {Roo.bootstrap.UploadCropbox} this
29048          */
29049         "download" : true,
29050         /**
29051          * @event footerbuttonclick
29052          * Fire when footerbuttonclick
29053          * @param {Roo.bootstrap.UploadCropbox} this
29054          * @param {String} type
29055          */
29056         "footerbuttonclick" : true,
29057         /**
29058          * @event resize
29059          * Fire when resize
29060          * @param {Roo.bootstrap.UploadCropbox} this
29061          */
29062         "resize" : true,
29063         /**
29064          * @event rotate
29065          * Fire when rotate the image
29066          * @param {Roo.bootstrap.UploadCropbox} this
29067          * @param {String} pos
29068          */
29069         "rotate" : true,
29070         /**
29071          * @event inspect
29072          * Fire when inspect the file
29073          * @param {Roo.bootstrap.UploadCropbox} this
29074          * @param {Object} file
29075          */
29076         "inspect" : true,
29077         /**
29078          * @event upload
29079          * Fire when xhr upload the file
29080          * @param {Roo.bootstrap.UploadCropbox} this
29081          * @param {Object} data
29082          */
29083         "upload" : true,
29084         /**
29085          * @event arrange
29086          * Fire when arrange the file data
29087          * @param {Roo.bootstrap.UploadCropbox} this
29088          * @param {Object} formData
29089          */
29090         "arrange" : true
29091     });
29092     
29093     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29094 };
29095
29096 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29097     
29098     emptyText : 'Click to upload image',
29099     rotateNotify : 'Image is too small to rotate',
29100     errorTimeout : 3000,
29101     scale : 0,
29102     baseScale : 1,
29103     rotate : 0,
29104     dragable : false,
29105     pinching : false,
29106     mouseX : 0,
29107     mouseY : 0,
29108     cropData : false,
29109     minWidth : 300,
29110     minHeight : 300,
29111     file : false,
29112     exif : {},
29113     baseRotate : 1,
29114     cropType : 'image/jpeg',
29115     buttons : false,
29116     canvasLoaded : false,
29117     isDocument : false,
29118     method : 'POST',
29119     paramName : 'imageUpload',
29120     loadMask : true,
29121     loadingText : 'Loading...',
29122     maskEl : false,
29123     
29124     getAutoCreate : function()
29125     {
29126         var cfg = {
29127             tag : 'div',
29128             cls : 'roo-upload-cropbox',
29129             cn : [
29130                 {
29131                     tag : 'input',
29132                     cls : 'roo-upload-cropbox-selector',
29133                     type : 'file'
29134                 },
29135                 {
29136                     tag : 'div',
29137                     cls : 'roo-upload-cropbox-body',
29138                     style : 'cursor:pointer',
29139                     cn : [
29140                         {
29141                             tag : 'div',
29142                             cls : 'roo-upload-cropbox-preview'
29143                         },
29144                         {
29145                             tag : 'div',
29146                             cls : 'roo-upload-cropbox-thumb'
29147                         },
29148                         {
29149                             tag : 'div',
29150                             cls : 'roo-upload-cropbox-empty-notify',
29151                             html : this.emptyText
29152                         },
29153                         {
29154                             tag : 'div',
29155                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29156                             html : this.rotateNotify
29157                         }
29158                     ]
29159                 },
29160                 {
29161                     tag : 'div',
29162                     cls : 'roo-upload-cropbox-footer',
29163                     cn : {
29164                         tag : 'div',
29165                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29166                         cn : []
29167                     }
29168                 }
29169             ]
29170         };
29171         
29172         return cfg;
29173     },
29174     
29175     onRender : function(ct, position)
29176     {
29177         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29178         
29179         if (this.buttons.length) {
29180             
29181             Roo.each(this.buttons, function(bb) {
29182                 
29183                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29184                 
29185                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29186                 
29187             }, this);
29188         }
29189         
29190         if(this.loadMask){
29191             this.maskEl = this.el;
29192         }
29193     },
29194     
29195     initEvents : function()
29196     {
29197         this.urlAPI = (window.createObjectURL && window) || 
29198                                 (window.URL && URL.revokeObjectURL && URL) || 
29199                                 (window.webkitURL && webkitURL);
29200                         
29201         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29202         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29203         
29204         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29205         this.selectorEl.hide();
29206         
29207         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29208         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29209         
29210         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29211         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29212         this.thumbEl.hide();
29213         
29214         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29215         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29216         
29217         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29218         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29219         this.errorEl.hide();
29220         
29221         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29222         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29223         this.footerEl.hide();
29224         
29225         this.setThumbBoxSize();
29226         
29227         this.bind();
29228         
29229         this.resize();
29230         
29231         this.fireEvent('initial', this);
29232     },
29233
29234     bind : function()
29235     {
29236         var _this = this;
29237         
29238         window.addEventListener("resize", function() { _this.resize(); } );
29239         
29240         this.bodyEl.on('click', this.beforeSelectFile, this);
29241         
29242         if(Roo.isTouch){
29243             this.bodyEl.on('touchstart', this.onTouchStart, this);
29244             this.bodyEl.on('touchmove', this.onTouchMove, this);
29245             this.bodyEl.on('touchend', this.onTouchEnd, this);
29246         }
29247         
29248         if(!Roo.isTouch){
29249             this.bodyEl.on('mousedown', this.onMouseDown, this);
29250             this.bodyEl.on('mousemove', this.onMouseMove, this);
29251             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29252             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29253             Roo.get(document).on('mouseup', this.onMouseUp, this);
29254         }
29255         
29256         this.selectorEl.on('change', this.onFileSelected, this);
29257     },
29258     
29259     reset : function()
29260     {    
29261         this.scale = 0;
29262         this.baseScale = 1;
29263         this.rotate = 0;
29264         this.baseRotate = 1;
29265         this.dragable = false;
29266         this.pinching = false;
29267         this.mouseX = 0;
29268         this.mouseY = 0;
29269         this.cropData = false;
29270         this.notifyEl.dom.innerHTML = this.emptyText;
29271         
29272         this.selectorEl.dom.value = '';
29273         
29274     },
29275     
29276     resize : function()
29277     {
29278         if(this.fireEvent('resize', this) != false){
29279             this.setThumbBoxPosition();
29280             this.setCanvasPosition();
29281         }
29282     },
29283     
29284     onFooterButtonClick : function(e, el, o, type)
29285     {
29286         switch (type) {
29287             case 'rotate-left' :
29288                 this.onRotateLeft(e);
29289                 break;
29290             case 'rotate-right' :
29291                 this.onRotateRight(e);
29292                 break;
29293             case 'picture' :
29294                 this.beforeSelectFile(e);
29295                 break;
29296             case 'trash' :
29297                 this.trash(e);
29298                 break;
29299             case 'crop' :
29300                 this.crop(e);
29301                 break;
29302             case 'download' :
29303                 this.download(e);
29304                 break;
29305             default :
29306                 break;
29307         }
29308         
29309         this.fireEvent('footerbuttonclick', this, type);
29310     },
29311     
29312     beforeSelectFile : function(e)
29313     {
29314         e.preventDefault();
29315         
29316         if(this.fireEvent('beforeselectfile', this) != false){
29317             this.selectorEl.dom.click();
29318         }
29319     },
29320     
29321     onFileSelected : function(e)
29322     {
29323         e.preventDefault();
29324         
29325         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29326             return;
29327         }
29328         
29329         var file = this.selectorEl.dom.files[0];
29330         
29331         if(this.fireEvent('inspect', this, file) != false){
29332             this.prepare(file);
29333         }
29334         
29335     },
29336     
29337     trash : function(e)
29338     {
29339         this.fireEvent('trash', this);
29340     },
29341     
29342     download : function(e)
29343     {
29344         this.fireEvent('download', this);
29345     },
29346     
29347     loadCanvas : function(src)
29348     {   
29349         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29350             
29351             this.reset();
29352             
29353             this.imageEl = document.createElement('img');
29354             
29355             var _this = this;
29356             
29357             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29358             
29359             this.imageEl.src = src;
29360         }
29361     },
29362     
29363     onLoadCanvas : function()
29364     {   
29365         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29366         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29367         
29368         this.bodyEl.un('click', this.beforeSelectFile, this);
29369         
29370         this.notifyEl.hide();
29371         this.thumbEl.show();
29372         this.footerEl.show();
29373         
29374         this.baseRotateLevel();
29375         
29376         if(this.isDocument){
29377             this.setThumbBoxSize();
29378         }
29379         
29380         this.setThumbBoxPosition();
29381         
29382         this.baseScaleLevel();
29383         
29384         this.draw();
29385         
29386         this.resize();
29387         
29388         this.canvasLoaded = true;
29389         
29390         if(this.loadMask){
29391             this.maskEl.unmask();
29392         }
29393         
29394     },
29395     
29396     setCanvasPosition : function()
29397     {   
29398         if(!this.canvasEl){
29399             return;
29400         }
29401         
29402         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29403         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29404         
29405         this.previewEl.setLeft(pw);
29406         this.previewEl.setTop(ph);
29407         
29408     },
29409     
29410     onMouseDown : function(e)
29411     {   
29412         e.stopEvent();
29413         
29414         this.dragable = true;
29415         this.pinching = false;
29416         
29417         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29418             this.dragable = false;
29419             return;
29420         }
29421         
29422         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29423         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29424         
29425     },
29426     
29427     onMouseMove : function(e)
29428     {   
29429         e.stopEvent();
29430         
29431         if(!this.canvasLoaded){
29432             return;
29433         }
29434         
29435         if (!this.dragable){
29436             return;
29437         }
29438         
29439         var minX = Math.ceil(this.thumbEl.getLeft(true));
29440         var minY = Math.ceil(this.thumbEl.getTop(true));
29441         
29442         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29443         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29444         
29445         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29446         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29447         
29448         x = x - this.mouseX;
29449         y = y - this.mouseY;
29450         
29451         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29452         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29453         
29454         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29455         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29456         
29457         this.previewEl.setLeft(bgX);
29458         this.previewEl.setTop(bgY);
29459         
29460         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29461         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29462     },
29463     
29464     onMouseUp : function(e)
29465     {   
29466         e.stopEvent();
29467         
29468         this.dragable = false;
29469     },
29470     
29471     onMouseWheel : function(e)
29472     {   
29473         e.stopEvent();
29474         
29475         this.startScale = this.scale;
29476         
29477         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29478         
29479         if(!this.zoomable()){
29480             this.scale = this.startScale;
29481             return;
29482         }
29483         
29484         this.draw();
29485         
29486         return;
29487     },
29488     
29489     zoomable : function()
29490     {
29491         var minScale = this.thumbEl.getWidth() / this.minWidth;
29492         
29493         if(this.minWidth < this.minHeight){
29494             minScale = this.thumbEl.getHeight() / this.minHeight;
29495         }
29496         
29497         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29498         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29499         
29500         if(
29501                 this.isDocument &&
29502                 (this.rotate == 0 || this.rotate == 180) && 
29503                 (
29504                     width > this.imageEl.OriginWidth || 
29505                     height > this.imageEl.OriginHeight ||
29506                     (width < this.minWidth && height < this.minHeight)
29507                 )
29508         ){
29509             return false;
29510         }
29511         
29512         if(
29513                 this.isDocument &&
29514                 (this.rotate == 90 || this.rotate == 270) && 
29515                 (
29516                     width > this.imageEl.OriginWidth || 
29517                     height > this.imageEl.OriginHeight ||
29518                     (width < this.minHeight && height < this.minWidth)
29519                 )
29520         ){
29521             return false;
29522         }
29523         
29524         if(
29525                 !this.isDocument &&
29526                 (this.rotate == 0 || this.rotate == 180) && 
29527                 (
29528                     width < this.minWidth || 
29529                     width > this.imageEl.OriginWidth || 
29530                     height < this.minHeight || 
29531                     height > this.imageEl.OriginHeight
29532                 )
29533         ){
29534             return false;
29535         }
29536         
29537         if(
29538                 !this.isDocument &&
29539                 (this.rotate == 90 || this.rotate == 270) && 
29540                 (
29541                     width < this.minHeight || 
29542                     width > this.imageEl.OriginWidth || 
29543                     height < this.minWidth || 
29544                     height > this.imageEl.OriginHeight
29545                 )
29546         ){
29547             return false;
29548         }
29549         
29550         return true;
29551         
29552     },
29553     
29554     onRotateLeft : function(e)
29555     {   
29556         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29557             
29558             var minScale = this.thumbEl.getWidth() / this.minWidth;
29559             
29560             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29561             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29562             
29563             this.startScale = this.scale;
29564             
29565             while (this.getScaleLevel() < minScale){
29566             
29567                 this.scale = this.scale + 1;
29568                 
29569                 if(!this.zoomable()){
29570                     break;
29571                 }
29572                 
29573                 if(
29574                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29575                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29576                 ){
29577                     continue;
29578                 }
29579                 
29580                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29581
29582                 this.draw();
29583                 
29584                 return;
29585             }
29586             
29587             this.scale = this.startScale;
29588             
29589             this.onRotateFail();
29590             
29591             return false;
29592         }
29593         
29594         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29595
29596         if(this.isDocument){
29597             this.setThumbBoxSize();
29598             this.setThumbBoxPosition();
29599             this.setCanvasPosition();
29600         }
29601         
29602         this.draw();
29603         
29604         this.fireEvent('rotate', this, 'left');
29605         
29606     },
29607     
29608     onRotateRight : function(e)
29609     {
29610         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29611             
29612             var minScale = this.thumbEl.getWidth() / this.minWidth;
29613         
29614             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29615             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29616             
29617             this.startScale = this.scale;
29618             
29619             while (this.getScaleLevel() < minScale){
29620             
29621                 this.scale = this.scale + 1;
29622                 
29623                 if(!this.zoomable()){
29624                     break;
29625                 }
29626                 
29627                 if(
29628                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29629                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29630                 ){
29631                     continue;
29632                 }
29633                 
29634                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29635
29636                 this.draw();
29637                 
29638                 return;
29639             }
29640             
29641             this.scale = this.startScale;
29642             
29643             this.onRotateFail();
29644             
29645             return false;
29646         }
29647         
29648         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29649
29650         if(this.isDocument){
29651             this.setThumbBoxSize();
29652             this.setThumbBoxPosition();
29653             this.setCanvasPosition();
29654         }
29655         
29656         this.draw();
29657         
29658         this.fireEvent('rotate', this, 'right');
29659     },
29660     
29661     onRotateFail : function()
29662     {
29663         this.errorEl.show(true);
29664         
29665         var _this = this;
29666         
29667         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29668     },
29669     
29670     draw : function()
29671     {
29672         this.previewEl.dom.innerHTML = '';
29673         
29674         var canvasEl = document.createElement("canvas");
29675         
29676         var contextEl = canvasEl.getContext("2d");
29677         
29678         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29679         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29680         var center = this.imageEl.OriginWidth / 2;
29681         
29682         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29683             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29684             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29685             center = this.imageEl.OriginHeight / 2;
29686         }
29687         
29688         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29689         
29690         contextEl.translate(center, center);
29691         contextEl.rotate(this.rotate * Math.PI / 180);
29692
29693         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29694         
29695         this.canvasEl = document.createElement("canvas");
29696         
29697         this.contextEl = this.canvasEl.getContext("2d");
29698         
29699         switch (this.rotate) {
29700             case 0 :
29701                 
29702                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29703                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29704                 
29705                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29706                 
29707                 break;
29708             case 90 : 
29709                 
29710                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29711                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29712                 
29713                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29714                     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);
29715                     break;
29716                 }
29717                 
29718                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29719                 
29720                 break;
29721             case 180 :
29722                 
29723                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29724                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29725                 
29726                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29727                     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);
29728                     break;
29729                 }
29730                 
29731                 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);
29732                 
29733                 break;
29734             case 270 :
29735                 
29736                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29737                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29738         
29739                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29740                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29741                     break;
29742                 }
29743                 
29744                 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);
29745                 
29746                 break;
29747             default : 
29748                 break;
29749         }
29750         
29751         this.previewEl.appendChild(this.canvasEl);
29752         
29753         this.setCanvasPosition();
29754     },
29755     
29756     crop : function()
29757     {
29758         if(!this.canvasLoaded){
29759             return;
29760         }
29761         
29762         var imageCanvas = document.createElement("canvas");
29763         
29764         var imageContext = imageCanvas.getContext("2d");
29765         
29766         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29767         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29768         
29769         var center = imageCanvas.width / 2;
29770         
29771         imageContext.translate(center, center);
29772         
29773         imageContext.rotate(this.rotate * Math.PI / 180);
29774         
29775         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29776         
29777         var canvas = document.createElement("canvas");
29778         
29779         var context = canvas.getContext("2d");
29780                 
29781         canvas.width = this.minWidth;
29782         canvas.height = this.minHeight;
29783
29784         switch (this.rotate) {
29785             case 0 :
29786                 
29787                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29788                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29789                 
29790                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29791                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29792                 
29793                 var targetWidth = this.minWidth - 2 * x;
29794                 var targetHeight = this.minHeight - 2 * y;
29795                 
29796                 var scale = 1;
29797                 
29798                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29799                     scale = targetWidth / width;
29800                 }
29801                 
29802                 if(x > 0 && y == 0){
29803                     scale = targetHeight / height;
29804                 }
29805                 
29806                 if(x > 0 && y > 0){
29807                     scale = targetWidth / width;
29808                     
29809                     if(width < height){
29810                         scale = targetHeight / height;
29811                     }
29812                 }
29813                 
29814                 context.scale(scale, scale);
29815                 
29816                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29817                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29818
29819                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29820                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29821
29822                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29823                 
29824                 break;
29825             case 90 : 
29826                 
29827                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29828                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29829                 
29830                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29831                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29832                 
29833                 var targetWidth = this.minWidth - 2 * x;
29834                 var targetHeight = this.minHeight - 2 * y;
29835                 
29836                 var scale = 1;
29837                 
29838                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29839                     scale = targetWidth / width;
29840                 }
29841                 
29842                 if(x > 0 && y == 0){
29843                     scale = targetHeight / height;
29844                 }
29845                 
29846                 if(x > 0 && y > 0){
29847                     scale = targetWidth / width;
29848                     
29849                     if(width < height){
29850                         scale = targetHeight / height;
29851                     }
29852                 }
29853                 
29854                 context.scale(scale, scale);
29855                 
29856                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29857                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29858
29859                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29860                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29861                 
29862                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29863                 
29864                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29865                 
29866                 break;
29867             case 180 :
29868                 
29869                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29870                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29871                 
29872                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29873                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29874                 
29875                 var targetWidth = this.minWidth - 2 * x;
29876                 var targetHeight = this.minHeight - 2 * y;
29877                 
29878                 var scale = 1;
29879                 
29880                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29881                     scale = targetWidth / width;
29882                 }
29883                 
29884                 if(x > 0 && y == 0){
29885                     scale = targetHeight / height;
29886                 }
29887                 
29888                 if(x > 0 && y > 0){
29889                     scale = targetWidth / width;
29890                     
29891                     if(width < height){
29892                         scale = targetHeight / height;
29893                     }
29894                 }
29895                 
29896                 context.scale(scale, scale);
29897                 
29898                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29899                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29900
29901                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29902                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29903
29904                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29905                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29906                 
29907                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29908                 
29909                 break;
29910             case 270 :
29911                 
29912                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29913                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29914                 
29915                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29916                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29917                 
29918                 var targetWidth = this.minWidth - 2 * x;
29919                 var targetHeight = this.minHeight - 2 * y;
29920                 
29921                 var scale = 1;
29922                 
29923                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29924                     scale = targetWidth / width;
29925                 }
29926                 
29927                 if(x > 0 && y == 0){
29928                     scale = targetHeight / height;
29929                 }
29930                 
29931                 if(x > 0 && y > 0){
29932                     scale = targetWidth / width;
29933                     
29934                     if(width < height){
29935                         scale = targetHeight / height;
29936                     }
29937                 }
29938                 
29939                 context.scale(scale, scale);
29940                 
29941                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29942                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29943
29944                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29945                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29946                 
29947                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29948                 
29949                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29950                 
29951                 break;
29952             default : 
29953                 break;
29954         }
29955         
29956         this.cropData = canvas.toDataURL(this.cropType);
29957         
29958         if(this.fireEvent('crop', this, this.cropData) !== false){
29959             this.process(this.file, this.cropData);
29960         }
29961         
29962         return;
29963         
29964     },
29965     
29966     setThumbBoxSize : function()
29967     {
29968         var width, height;
29969         
29970         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
29971             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
29972             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
29973             
29974             this.minWidth = width;
29975             this.minHeight = height;
29976             
29977             if(this.rotate == 90 || this.rotate == 270){
29978                 this.minWidth = height;
29979                 this.minHeight = width;
29980             }
29981         }
29982         
29983         height = 300;
29984         width = Math.ceil(this.minWidth * height / this.minHeight);
29985         
29986         if(this.minWidth > this.minHeight){
29987             width = 300;
29988             height = Math.ceil(this.minHeight * width / this.minWidth);
29989         }
29990         
29991         this.thumbEl.setStyle({
29992             width : width + 'px',
29993             height : height + 'px'
29994         });
29995
29996         return;
29997             
29998     },
29999     
30000     setThumbBoxPosition : function()
30001     {
30002         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30003         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30004         
30005         this.thumbEl.setLeft(x);
30006         this.thumbEl.setTop(y);
30007         
30008     },
30009     
30010     baseRotateLevel : function()
30011     {
30012         this.baseRotate = 1;
30013         
30014         if(
30015                 typeof(this.exif) != 'undefined' &&
30016                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30017                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30018         ){
30019             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30020         }
30021         
30022         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30023         
30024     },
30025     
30026     baseScaleLevel : function()
30027     {
30028         var width, height;
30029         
30030         if(this.isDocument){
30031             
30032             if(this.baseRotate == 6 || this.baseRotate == 8){
30033             
30034                 height = this.thumbEl.getHeight();
30035                 this.baseScale = height / this.imageEl.OriginWidth;
30036
30037                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30038                     width = this.thumbEl.getWidth();
30039                     this.baseScale = width / this.imageEl.OriginHeight;
30040                 }
30041
30042                 return;
30043             }
30044
30045             height = this.thumbEl.getHeight();
30046             this.baseScale = height / this.imageEl.OriginHeight;
30047
30048             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30049                 width = this.thumbEl.getWidth();
30050                 this.baseScale = width / this.imageEl.OriginWidth;
30051             }
30052
30053             return;
30054         }
30055         
30056         if(this.baseRotate == 6 || this.baseRotate == 8){
30057             
30058             width = this.thumbEl.getHeight();
30059             this.baseScale = width / this.imageEl.OriginHeight;
30060             
30061             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30062                 height = this.thumbEl.getWidth();
30063                 this.baseScale = height / this.imageEl.OriginHeight;
30064             }
30065             
30066             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30067                 height = this.thumbEl.getWidth();
30068                 this.baseScale = height / this.imageEl.OriginHeight;
30069                 
30070                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30071                     width = this.thumbEl.getHeight();
30072                     this.baseScale = width / this.imageEl.OriginWidth;
30073                 }
30074             }
30075             
30076             return;
30077         }
30078         
30079         width = this.thumbEl.getWidth();
30080         this.baseScale = width / this.imageEl.OriginWidth;
30081         
30082         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30083             height = this.thumbEl.getHeight();
30084             this.baseScale = height / this.imageEl.OriginHeight;
30085         }
30086         
30087         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30088             
30089             height = this.thumbEl.getHeight();
30090             this.baseScale = height / this.imageEl.OriginHeight;
30091             
30092             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30093                 width = this.thumbEl.getWidth();
30094                 this.baseScale = width / this.imageEl.OriginWidth;
30095             }
30096             
30097         }
30098         
30099         return;
30100     },
30101     
30102     getScaleLevel : function()
30103     {
30104         return this.baseScale * Math.pow(1.1, this.scale);
30105     },
30106     
30107     onTouchStart : function(e)
30108     {
30109         if(!this.canvasLoaded){
30110             this.beforeSelectFile(e);
30111             return;
30112         }
30113         
30114         var touches = e.browserEvent.touches;
30115         
30116         if(!touches){
30117             return;
30118         }
30119         
30120         if(touches.length == 1){
30121             this.onMouseDown(e);
30122             return;
30123         }
30124         
30125         if(touches.length != 2){
30126             return;
30127         }
30128         
30129         var coords = [];
30130         
30131         for(var i = 0, finger; finger = touches[i]; i++){
30132             coords.push(finger.pageX, finger.pageY);
30133         }
30134         
30135         var x = Math.pow(coords[0] - coords[2], 2);
30136         var y = Math.pow(coords[1] - coords[3], 2);
30137         
30138         this.startDistance = Math.sqrt(x + y);
30139         
30140         this.startScale = this.scale;
30141         
30142         this.pinching = true;
30143         this.dragable = false;
30144         
30145     },
30146     
30147     onTouchMove : function(e)
30148     {
30149         if(!this.pinching && !this.dragable){
30150             return;
30151         }
30152         
30153         var touches = e.browserEvent.touches;
30154         
30155         if(!touches){
30156             return;
30157         }
30158         
30159         if(this.dragable){
30160             this.onMouseMove(e);
30161             return;
30162         }
30163         
30164         var coords = [];
30165         
30166         for(var i = 0, finger; finger = touches[i]; i++){
30167             coords.push(finger.pageX, finger.pageY);
30168         }
30169         
30170         var x = Math.pow(coords[0] - coords[2], 2);
30171         var y = Math.pow(coords[1] - coords[3], 2);
30172         
30173         this.endDistance = Math.sqrt(x + y);
30174         
30175         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30176         
30177         if(!this.zoomable()){
30178             this.scale = this.startScale;
30179             return;
30180         }
30181         
30182         this.draw();
30183         
30184     },
30185     
30186     onTouchEnd : function(e)
30187     {
30188         this.pinching = false;
30189         this.dragable = false;
30190         
30191     },
30192     
30193     process : function(file, crop)
30194     {
30195         if(this.loadMask){
30196             this.maskEl.mask(this.loadingText);
30197         }
30198         
30199         this.xhr = new XMLHttpRequest();
30200         
30201         file.xhr = this.xhr;
30202
30203         this.xhr.open(this.method, this.url, true);
30204         
30205         var headers = {
30206             "Accept": "application/json",
30207             "Cache-Control": "no-cache",
30208             "X-Requested-With": "XMLHttpRequest"
30209         };
30210         
30211         for (var headerName in headers) {
30212             var headerValue = headers[headerName];
30213             if (headerValue) {
30214                 this.xhr.setRequestHeader(headerName, headerValue);
30215             }
30216         }
30217         
30218         var _this = this;
30219         
30220         this.xhr.onload = function()
30221         {
30222             _this.xhrOnLoad(_this.xhr);
30223         }
30224         
30225         this.xhr.onerror = function()
30226         {
30227             _this.xhrOnError(_this.xhr);
30228         }
30229         
30230         var formData = new FormData();
30231
30232         formData.append('returnHTML', 'NO');
30233         
30234         if(crop){
30235             formData.append('crop', crop);
30236         }
30237         
30238         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30239             formData.append(this.paramName, file, file.name);
30240         }
30241         
30242         if(typeof(file.filename) != 'undefined'){
30243             formData.append('filename', file.filename);
30244         }
30245         
30246         if(typeof(file.mimetype) != 'undefined'){
30247             formData.append('mimetype', file.mimetype);
30248         }
30249         
30250         if(this.fireEvent('arrange', this, formData) != false){
30251             this.xhr.send(formData);
30252         };
30253     },
30254     
30255     xhrOnLoad : function(xhr)
30256     {
30257         if(this.loadMask){
30258             this.maskEl.unmask();
30259         }
30260         
30261         if (xhr.readyState !== 4) {
30262             this.fireEvent('exception', this, xhr);
30263             return;
30264         }
30265
30266         var response = Roo.decode(xhr.responseText);
30267         
30268         if(!response.success){
30269             this.fireEvent('exception', this, xhr);
30270             return;
30271         }
30272         
30273         var response = Roo.decode(xhr.responseText);
30274         
30275         this.fireEvent('upload', this, response);
30276         
30277     },
30278     
30279     xhrOnError : function()
30280     {
30281         if(this.loadMask){
30282             this.maskEl.unmask();
30283         }
30284         
30285         Roo.log('xhr on error');
30286         
30287         var response = Roo.decode(xhr.responseText);
30288           
30289         Roo.log(response);
30290         
30291     },
30292     
30293     prepare : function(file)
30294     {   
30295         if(this.loadMask){
30296             this.maskEl.mask(this.loadingText);
30297         }
30298         
30299         this.file = false;
30300         this.exif = {};
30301         
30302         if(typeof(file) === 'string'){
30303             this.loadCanvas(file);
30304             return;
30305         }
30306         
30307         if(!file || !this.urlAPI){
30308             return;
30309         }
30310         
30311         this.file = file;
30312         this.cropType = file.type;
30313         
30314         var _this = this;
30315         
30316         if(this.fireEvent('prepare', this, this.file) != false){
30317             
30318             var reader = new FileReader();
30319             
30320             reader.onload = function (e) {
30321                 if (e.target.error) {
30322                     Roo.log(e.target.error);
30323                     return;
30324                 }
30325                 
30326                 var buffer = e.target.result,
30327                     dataView = new DataView(buffer),
30328                     offset = 2,
30329                     maxOffset = dataView.byteLength - 4,
30330                     markerBytes,
30331                     markerLength;
30332                 
30333                 if (dataView.getUint16(0) === 0xffd8) {
30334                     while (offset < maxOffset) {
30335                         markerBytes = dataView.getUint16(offset);
30336                         
30337                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30338                             markerLength = dataView.getUint16(offset + 2) + 2;
30339                             if (offset + markerLength > dataView.byteLength) {
30340                                 Roo.log('Invalid meta data: Invalid segment size.');
30341                                 break;
30342                             }
30343                             
30344                             if(markerBytes == 0xffe1){
30345                                 _this.parseExifData(
30346                                     dataView,
30347                                     offset,
30348                                     markerLength
30349                                 );
30350                             }
30351                             
30352                             offset += markerLength;
30353                             
30354                             continue;
30355                         }
30356                         
30357                         break;
30358                     }
30359                     
30360                 }
30361                 
30362                 var url = _this.urlAPI.createObjectURL(_this.file);
30363                 
30364                 _this.loadCanvas(url);
30365                 
30366                 return;
30367             }
30368             
30369             reader.readAsArrayBuffer(this.file);
30370             
30371         }
30372         
30373     },
30374     
30375     parseExifData : function(dataView, offset, length)
30376     {
30377         var tiffOffset = offset + 10,
30378             littleEndian,
30379             dirOffset;
30380     
30381         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30382             // No Exif data, might be XMP data instead
30383             return;
30384         }
30385         
30386         // Check for the ASCII code for "Exif" (0x45786966):
30387         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30388             // No Exif data, might be XMP data instead
30389             return;
30390         }
30391         if (tiffOffset + 8 > dataView.byteLength) {
30392             Roo.log('Invalid Exif data: Invalid segment size.');
30393             return;
30394         }
30395         // Check for the two null bytes:
30396         if (dataView.getUint16(offset + 8) !== 0x0000) {
30397             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30398             return;
30399         }
30400         // Check the byte alignment:
30401         switch (dataView.getUint16(tiffOffset)) {
30402         case 0x4949:
30403             littleEndian = true;
30404             break;
30405         case 0x4D4D:
30406             littleEndian = false;
30407             break;
30408         default:
30409             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30410             return;
30411         }
30412         // Check for the TIFF tag marker (0x002A):
30413         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30414             Roo.log('Invalid Exif data: Missing TIFF marker.');
30415             return;
30416         }
30417         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30418         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30419         
30420         this.parseExifTags(
30421             dataView,
30422             tiffOffset,
30423             tiffOffset + dirOffset,
30424             littleEndian
30425         );
30426     },
30427     
30428     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30429     {
30430         var tagsNumber,
30431             dirEndOffset,
30432             i;
30433         if (dirOffset + 6 > dataView.byteLength) {
30434             Roo.log('Invalid Exif data: Invalid directory offset.');
30435             return;
30436         }
30437         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30438         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30439         if (dirEndOffset + 4 > dataView.byteLength) {
30440             Roo.log('Invalid Exif data: Invalid directory size.');
30441             return;
30442         }
30443         for (i = 0; i < tagsNumber; i += 1) {
30444             this.parseExifTag(
30445                 dataView,
30446                 tiffOffset,
30447                 dirOffset + 2 + 12 * i, // tag offset
30448                 littleEndian
30449             );
30450         }
30451         // Return the offset to the next directory:
30452         return dataView.getUint32(dirEndOffset, littleEndian);
30453     },
30454     
30455     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30456     {
30457         var tag = dataView.getUint16(offset, littleEndian);
30458         
30459         this.exif[tag] = this.getExifValue(
30460             dataView,
30461             tiffOffset,
30462             offset,
30463             dataView.getUint16(offset + 2, littleEndian), // tag type
30464             dataView.getUint32(offset + 4, littleEndian), // tag length
30465             littleEndian
30466         );
30467     },
30468     
30469     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30470     {
30471         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30472             tagSize,
30473             dataOffset,
30474             values,
30475             i,
30476             str,
30477             c;
30478     
30479         if (!tagType) {
30480             Roo.log('Invalid Exif data: Invalid tag type.');
30481             return;
30482         }
30483         
30484         tagSize = tagType.size * length;
30485         // Determine if the value is contained in the dataOffset bytes,
30486         // or if the value at the dataOffset is a pointer to the actual data:
30487         dataOffset = tagSize > 4 ?
30488                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30489         if (dataOffset + tagSize > dataView.byteLength) {
30490             Roo.log('Invalid Exif data: Invalid data offset.');
30491             return;
30492         }
30493         if (length === 1) {
30494             return tagType.getValue(dataView, dataOffset, littleEndian);
30495         }
30496         values = [];
30497         for (i = 0; i < length; i += 1) {
30498             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30499         }
30500         
30501         if (tagType.ascii) {
30502             str = '';
30503             // Concatenate the chars:
30504             for (i = 0; i < values.length; i += 1) {
30505                 c = values[i];
30506                 // Ignore the terminating NULL byte(s):
30507                 if (c === '\u0000') {
30508                     break;
30509                 }
30510                 str += c;
30511             }
30512             return str;
30513         }
30514         return values;
30515     }
30516     
30517 });
30518
30519 Roo.apply(Roo.bootstrap.UploadCropbox, {
30520     tags : {
30521         'Orientation': 0x0112
30522     },
30523     
30524     Orientation: {
30525             1: 0, //'top-left',
30526 //            2: 'top-right',
30527             3: 180, //'bottom-right',
30528 //            4: 'bottom-left',
30529 //            5: 'left-top',
30530             6: 90, //'right-top',
30531 //            7: 'right-bottom',
30532             8: 270 //'left-bottom'
30533     },
30534     
30535     exifTagTypes : {
30536         // byte, 8-bit unsigned int:
30537         1: {
30538             getValue: function (dataView, dataOffset) {
30539                 return dataView.getUint8(dataOffset);
30540             },
30541             size: 1
30542         },
30543         // ascii, 8-bit byte:
30544         2: {
30545             getValue: function (dataView, dataOffset) {
30546                 return String.fromCharCode(dataView.getUint8(dataOffset));
30547             },
30548             size: 1,
30549             ascii: true
30550         },
30551         // short, 16 bit int:
30552         3: {
30553             getValue: function (dataView, dataOffset, littleEndian) {
30554                 return dataView.getUint16(dataOffset, littleEndian);
30555             },
30556             size: 2
30557         },
30558         // long, 32 bit int:
30559         4: {
30560             getValue: function (dataView, dataOffset, littleEndian) {
30561                 return dataView.getUint32(dataOffset, littleEndian);
30562             },
30563             size: 4
30564         },
30565         // rational = two long values, first is numerator, second is denominator:
30566         5: {
30567             getValue: function (dataView, dataOffset, littleEndian) {
30568                 return dataView.getUint32(dataOffset, littleEndian) /
30569                     dataView.getUint32(dataOffset + 4, littleEndian);
30570             },
30571             size: 8
30572         },
30573         // slong, 32 bit signed int:
30574         9: {
30575             getValue: function (dataView, dataOffset, littleEndian) {
30576                 return dataView.getInt32(dataOffset, littleEndian);
30577             },
30578             size: 4
30579         },
30580         // srational, two slongs, first is numerator, second is denominator:
30581         10: {
30582             getValue: function (dataView, dataOffset, littleEndian) {
30583                 return dataView.getInt32(dataOffset, littleEndian) /
30584                     dataView.getInt32(dataOffset + 4, littleEndian);
30585             },
30586             size: 8
30587         }
30588     },
30589     
30590     footer : {
30591         STANDARD : [
30592             {
30593                 tag : 'div',
30594                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30595                 action : 'rotate-left',
30596                 cn : [
30597                     {
30598                         tag : 'button',
30599                         cls : 'btn btn-default',
30600                         html : '<i class="fa fa-undo"></i>'
30601                     }
30602                 ]
30603             },
30604             {
30605                 tag : 'div',
30606                 cls : 'btn-group roo-upload-cropbox-picture',
30607                 action : 'picture',
30608                 cn : [
30609                     {
30610                         tag : 'button',
30611                         cls : 'btn btn-default',
30612                         html : '<i class="fa fa-picture-o"></i>'
30613                     }
30614                 ]
30615             },
30616             {
30617                 tag : 'div',
30618                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30619                 action : 'rotate-right',
30620                 cn : [
30621                     {
30622                         tag : 'button',
30623                         cls : 'btn btn-default',
30624                         html : '<i class="fa fa-repeat"></i>'
30625                     }
30626                 ]
30627             }
30628         ],
30629         DOCUMENT : [
30630             {
30631                 tag : 'div',
30632                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30633                 action : 'rotate-left',
30634                 cn : [
30635                     {
30636                         tag : 'button',
30637                         cls : 'btn btn-default',
30638                         html : '<i class="fa fa-undo"></i>'
30639                     }
30640                 ]
30641             },
30642             {
30643                 tag : 'div',
30644                 cls : 'btn-group roo-upload-cropbox-download',
30645                 action : 'download',
30646                 cn : [
30647                     {
30648                         tag : 'button',
30649                         cls : 'btn btn-default',
30650                         html : '<i class="fa fa-download"></i>'
30651                     }
30652                 ]
30653             },
30654             {
30655                 tag : 'div',
30656                 cls : 'btn-group roo-upload-cropbox-crop',
30657                 action : 'crop',
30658                 cn : [
30659                     {
30660                         tag : 'button',
30661                         cls : 'btn btn-default',
30662                         html : '<i class="fa fa-crop"></i>'
30663                     }
30664                 ]
30665             },
30666             {
30667                 tag : 'div',
30668                 cls : 'btn-group roo-upload-cropbox-trash',
30669                 action : 'trash',
30670                 cn : [
30671                     {
30672                         tag : 'button',
30673                         cls : 'btn btn-default',
30674                         html : '<i class="fa fa-trash"></i>'
30675                     }
30676                 ]
30677             },
30678             {
30679                 tag : 'div',
30680                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30681                 action : 'rotate-right',
30682                 cn : [
30683                     {
30684                         tag : 'button',
30685                         cls : 'btn btn-default',
30686                         html : '<i class="fa fa-repeat"></i>'
30687                     }
30688                 ]
30689             }
30690         ],
30691         ROTATOR : [
30692             {
30693                 tag : 'div',
30694                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30695                 action : 'rotate-left',
30696                 cn : [
30697                     {
30698                         tag : 'button',
30699                         cls : 'btn btn-default',
30700                         html : '<i class="fa fa-undo"></i>'
30701                     }
30702                 ]
30703             },
30704             {
30705                 tag : 'div',
30706                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30707                 action : 'rotate-right',
30708                 cn : [
30709                     {
30710                         tag : 'button',
30711                         cls : 'btn btn-default',
30712                         html : '<i class="fa fa-repeat"></i>'
30713                     }
30714                 ]
30715             }
30716         ]
30717     }
30718 });
30719
30720 /*
30721 * Licence: LGPL
30722 */
30723
30724 /**
30725  * @class Roo.bootstrap.DocumentManager
30726  * @extends Roo.bootstrap.Component
30727  * Bootstrap DocumentManager class
30728  * @cfg {String} paramName default 'imageUpload'
30729  * @cfg {String} toolTipName default 'filename'
30730  * @cfg {String} method default POST
30731  * @cfg {String} url action url
30732  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30733  * @cfg {Boolean} multiple multiple upload default true
30734  * @cfg {Number} thumbSize default 300
30735  * @cfg {String} fieldLabel
30736  * @cfg {Number} labelWidth default 4
30737  * @cfg {String} labelAlign (left|top) default left
30738  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30739 * @cfg {Number} labellg set the width of label (1-12)
30740  * @cfg {Number} labelmd set the width of label (1-12)
30741  * @cfg {Number} labelsm set the width of label (1-12)
30742  * @cfg {Number} labelxs set the width of label (1-12)
30743  * 
30744  * @constructor
30745  * Create a new DocumentManager
30746  * @param {Object} config The config object
30747  */
30748
30749 Roo.bootstrap.DocumentManager = function(config){
30750     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30751     
30752     this.files = [];
30753     this.delegates = [];
30754     
30755     this.addEvents({
30756         /**
30757          * @event initial
30758          * Fire when initial the DocumentManager
30759          * @param {Roo.bootstrap.DocumentManager} this
30760          */
30761         "initial" : true,
30762         /**
30763          * @event inspect
30764          * inspect selected file
30765          * @param {Roo.bootstrap.DocumentManager} this
30766          * @param {File} file
30767          */
30768         "inspect" : true,
30769         /**
30770          * @event exception
30771          * Fire when xhr load exception
30772          * @param {Roo.bootstrap.DocumentManager} this
30773          * @param {XMLHttpRequest} xhr
30774          */
30775         "exception" : true,
30776         /**
30777          * @event afterupload
30778          * Fire when xhr load exception
30779          * @param {Roo.bootstrap.DocumentManager} this
30780          * @param {XMLHttpRequest} xhr
30781          */
30782         "afterupload" : true,
30783         /**
30784          * @event prepare
30785          * prepare the form data
30786          * @param {Roo.bootstrap.DocumentManager} this
30787          * @param {Object} formData
30788          */
30789         "prepare" : true,
30790         /**
30791          * @event remove
30792          * Fire when remove the file
30793          * @param {Roo.bootstrap.DocumentManager} this
30794          * @param {Object} file
30795          */
30796         "remove" : true,
30797         /**
30798          * @event refresh
30799          * Fire after refresh the file
30800          * @param {Roo.bootstrap.DocumentManager} this
30801          */
30802         "refresh" : true,
30803         /**
30804          * @event click
30805          * Fire after click the image
30806          * @param {Roo.bootstrap.DocumentManager} this
30807          * @param {Object} file
30808          */
30809         "click" : true,
30810         /**
30811          * @event edit
30812          * Fire when upload a image and editable set to true
30813          * @param {Roo.bootstrap.DocumentManager} this
30814          * @param {Object} file
30815          */
30816         "edit" : true,
30817         /**
30818          * @event beforeselectfile
30819          * Fire before select file
30820          * @param {Roo.bootstrap.DocumentManager} this
30821          */
30822         "beforeselectfile" : true,
30823         /**
30824          * @event process
30825          * Fire before process file
30826          * @param {Roo.bootstrap.DocumentManager} this
30827          * @param {Object} file
30828          */
30829         "process" : true,
30830         /**
30831          * @event previewrendered
30832          * Fire when preview rendered
30833          * @param {Roo.bootstrap.DocumentManager} this
30834          * @param {Object} file
30835          */
30836         "previewrendered" : true,
30837         /**
30838          */
30839         "previewResize" : true
30840         
30841     });
30842 };
30843
30844 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
30845     
30846     boxes : 0,
30847     inputName : '',
30848     thumbSize : 300,
30849     multiple : true,
30850     files : false,
30851     method : 'POST',
30852     url : '',
30853     paramName : 'imageUpload',
30854     toolTipName : 'filename',
30855     fieldLabel : '',
30856     labelWidth : 4,
30857     labelAlign : 'left',
30858     editable : true,
30859     delegates : false,
30860     xhr : false, 
30861     
30862     labellg : 0,
30863     labelmd : 0,
30864     labelsm : 0,
30865     labelxs : 0,
30866     
30867     getAutoCreate : function()
30868     {   
30869         var managerWidget = {
30870             tag : 'div',
30871             cls : 'roo-document-manager',
30872             cn : [
30873                 {
30874                     tag : 'input',
30875                     cls : 'roo-document-manager-selector',
30876                     type : 'file'
30877                 },
30878                 {
30879                     tag : 'div',
30880                     cls : 'roo-document-manager-uploader',
30881                     cn : [
30882                         {
30883                             tag : 'div',
30884                             cls : 'roo-document-manager-upload-btn',
30885                             html : '<i class="fa fa-plus"></i>'
30886                         }
30887                     ]
30888                     
30889                 }
30890             ]
30891         };
30892         
30893         var content = [
30894             {
30895                 tag : 'div',
30896                 cls : 'column col-md-12',
30897                 cn : managerWidget
30898             }
30899         ];
30900         
30901         if(this.fieldLabel.length){
30902             
30903             content = [
30904                 {
30905                     tag : 'div',
30906                     cls : 'column col-md-12',
30907                     html : this.fieldLabel
30908                 },
30909                 {
30910                     tag : 'div',
30911                     cls : 'column col-md-12',
30912                     cn : managerWidget
30913                 }
30914             ];
30915
30916             if(this.labelAlign == 'left'){
30917                 content = [
30918                     {
30919                         tag : 'div',
30920                         cls : 'column',
30921                         html : this.fieldLabel
30922                     },
30923                     {
30924                         tag : 'div',
30925                         cls : 'column',
30926                         cn : managerWidget
30927                     }
30928                 ];
30929                 
30930                 if(this.labelWidth > 12){
30931                     content[0].style = "width: " + this.labelWidth + 'px';
30932                 }
30933
30934                 if(this.labelWidth < 13 && this.labelmd == 0){
30935                     this.labelmd = this.labelWidth;
30936                 }
30937
30938                 if(this.labellg > 0){
30939                     content[0].cls += ' col-lg-' + this.labellg;
30940                     content[1].cls += ' col-lg-' + (12 - this.labellg);
30941                 }
30942
30943                 if(this.labelmd > 0){
30944                     content[0].cls += ' col-md-' + this.labelmd;
30945                     content[1].cls += ' col-md-' + (12 - this.labelmd);
30946                 }
30947
30948                 if(this.labelsm > 0){
30949                     content[0].cls += ' col-sm-' + this.labelsm;
30950                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
30951                 }
30952
30953                 if(this.labelxs > 0){
30954                     content[0].cls += ' col-xs-' + this.labelxs;
30955                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
30956                 }
30957                 
30958             }
30959         }
30960         
30961         var cfg = {
30962             tag : 'div',
30963             cls : 'row clearfix',
30964             cn : content
30965         };
30966         
30967         return cfg;
30968         
30969     },
30970     
30971     initEvents : function()
30972     {
30973         this.managerEl = this.el.select('.roo-document-manager', true).first();
30974         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30975         
30976         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
30977         this.selectorEl.hide();
30978         
30979         if(this.multiple){
30980             this.selectorEl.attr('multiple', 'multiple');
30981         }
30982         
30983         this.selectorEl.on('change', this.onFileSelected, this);
30984         
30985         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
30986         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30987         
30988         this.uploader.on('click', this.onUploaderClick, this);
30989         
30990         this.renderProgressDialog();
30991         
30992         var _this = this;
30993         
30994         window.addEventListener("resize", function() { _this.refresh(); } );
30995         
30996         this.fireEvent('initial', this);
30997     },
30998     
30999     renderProgressDialog : function()
31000     {
31001         var _this = this;
31002         
31003         this.progressDialog = new Roo.bootstrap.Modal({
31004             cls : 'roo-document-manager-progress-dialog',
31005             allow_close : false,
31006             animate : false,
31007             title : '',
31008             buttons : [
31009                 {
31010                     name  :'cancel',
31011                     weight : 'danger',
31012                     html : 'Cancel'
31013                 }
31014             ], 
31015             listeners : { 
31016                 btnclick : function() {
31017                     _this.uploadCancel();
31018                     this.hide();
31019                 }
31020             }
31021         });
31022          
31023         this.progressDialog.render(Roo.get(document.body));
31024          
31025         this.progress = new Roo.bootstrap.Progress({
31026             cls : 'roo-document-manager-progress',
31027             active : true,
31028             striped : true
31029         });
31030         
31031         this.progress.render(this.progressDialog.getChildContainer());
31032         
31033         this.progressBar = new Roo.bootstrap.ProgressBar({
31034             cls : 'roo-document-manager-progress-bar',
31035             aria_valuenow : 0,
31036             aria_valuemin : 0,
31037             aria_valuemax : 12,
31038             panel : 'success'
31039         });
31040         
31041         this.progressBar.render(this.progress.getChildContainer());
31042     },
31043     
31044     onUploaderClick : function(e)
31045     {
31046         e.preventDefault();
31047      
31048         if(this.fireEvent('beforeselectfile', this) != false){
31049             this.selectorEl.dom.click();
31050         }
31051         
31052     },
31053     
31054     onFileSelected : function(e)
31055     {
31056         e.preventDefault();
31057         
31058         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31059             return;
31060         }
31061         
31062         Roo.each(this.selectorEl.dom.files, function(file){
31063             if(this.fireEvent('inspect', this, file) != false){
31064                 this.files.push(file);
31065             }
31066         }, this);
31067         
31068         this.queue();
31069         
31070     },
31071     
31072     queue : function()
31073     {
31074         this.selectorEl.dom.value = '';
31075         
31076         if(!this.files || !this.files.length){
31077             return;
31078         }
31079         
31080         if(this.boxes > 0 && this.files.length > this.boxes){
31081             this.files = this.files.slice(0, this.boxes);
31082         }
31083         
31084         this.uploader.show();
31085         
31086         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31087             this.uploader.hide();
31088         }
31089         
31090         var _this = this;
31091         
31092         var files = [];
31093         
31094         var docs = [];
31095         
31096         Roo.each(this.files, function(file){
31097             
31098             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31099                 var f = this.renderPreview(file);
31100                 files.push(f);
31101                 return;
31102             }
31103             
31104             if(file.type.indexOf('image') != -1){
31105                 this.delegates.push(
31106                     (function(){
31107                         _this.process(file);
31108                     }).createDelegate(this)
31109                 );
31110         
31111                 return;
31112             }
31113             
31114             docs.push(
31115                 (function(){
31116                     _this.process(file);
31117                 }).createDelegate(this)
31118             );
31119             
31120         }, this);
31121         
31122         this.files = files;
31123         
31124         this.delegates = this.delegates.concat(docs);
31125         
31126         if(!this.delegates.length){
31127             this.refresh();
31128             return;
31129         }
31130         
31131         this.progressBar.aria_valuemax = this.delegates.length;
31132         
31133         this.arrange();
31134         
31135         return;
31136     },
31137     
31138     arrange : function()
31139     {
31140         if(!this.delegates.length){
31141             this.progressDialog.hide();
31142             this.refresh();
31143             return;
31144         }
31145         
31146         var delegate = this.delegates.shift();
31147         
31148         this.progressDialog.show();
31149         
31150         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31151         
31152         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31153         
31154         delegate();
31155     },
31156     
31157     refresh : function()
31158     {
31159         this.uploader.show();
31160         
31161         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31162             this.uploader.hide();
31163         }
31164         
31165         Roo.isTouch ? this.closable(false) : this.closable(true);
31166         
31167         this.fireEvent('refresh', this);
31168     },
31169     
31170     onRemove : function(e, el, o)
31171     {
31172         e.preventDefault();
31173         
31174         this.fireEvent('remove', this, o);
31175         
31176     },
31177     
31178     remove : function(o)
31179     {
31180         var files = [];
31181         
31182         Roo.each(this.files, function(file){
31183             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31184                 files.push(file);
31185                 return;
31186             }
31187
31188             o.target.remove();
31189
31190         }, this);
31191         
31192         this.files = files;
31193         
31194         this.refresh();
31195     },
31196     
31197     clear : function()
31198     {
31199         Roo.each(this.files, function(file){
31200             if(!file.target){
31201                 return;
31202             }
31203             
31204             file.target.remove();
31205
31206         }, this);
31207         
31208         this.files = [];
31209         
31210         this.refresh();
31211     },
31212     
31213     onClick : function(e, el, o)
31214     {
31215         e.preventDefault();
31216         
31217         this.fireEvent('click', this, o);
31218         
31219     },
31220     
31221     closable : function(closable)
31222     {
31223         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31224             
31225             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31226             
31227             if(closable){
31228                 el.show();
31229                 return;
31230             }
31231             
31232             el.hide();
31233             
31234         }, this);
31235     },
31236     
31237     xhrOnLoad : function(xhr)
31238     {
31239         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31240             el.remove();
31241         }, this);
31242         
31243         if (xhr.readyState !== 4) {
31244             this.arrange();
31245             this.fireEvent('exception', this, xhr);
31246             return;
31247         }
31248
31249         var response = Roo.decode(xhr.responseText);
31250         
31251         if(!response.success){
31252             this.arrange();
31253             this.fireEvent('exception', this, xhr);
31254             return;
31255         }
31256         
31257         var file = this.renderPreview(response.data);
31258         
31259         this.files.push(file);
31260         
31261         this.arrange();
31262         
31263         this.fireEvent('afterupload', this, xhr);
31264         
31265     },
31266     
31267     xhrOnError : function(xhr)
31268     {
31269         Roo.log('xhr on error');
31270         
31271         var response = Roo.decode(xhr.responseText);
31272           
31273         Roo.log(response);
31274         
31275         this.arrange();
31276     },
31277     
31278     process : function(file)
31279     {
31280         if(this.fireEvent('process', this, file) !== false){
31281             if(this.editable && file.type.indexOf('image') != -1){
31282                 this.fireEvent('edit', this, file);
31283                 return;
31284             }
31285
31286             this.uploadStart(file, false);
31287
31288             return;
31289         }
31290         
31291     },
31292     
31293     uploadStart : function(file, crop)
31294     {
31295         this.xhr = new XMLHttpRequest();
31296         
31297         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31298             this.arrange();
31299             return;
31300         }
31301         
31302         file.xhr = this.xhr;
31303             
31304         this.managerEl.createChild({
31305             tag : 'div',
31306             cls : 'roo-document-manager-loading',
31307             cn : [
31308                 {
31309                     tag : 'div',
31310                     tooltip : file.name,
31311                     cls : 'roo-document-manager-thumb',
31312                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31313                 }
31314             ]
31315
31316         });
31317
31318         this.xhr.open(this.method, this.url, true);
31319         
31320         var headers = {
31321             "Accept": "application/json",
31322             "Cache-Control": "no-cache",
31323             "X-Requested-With": "XMLHttpRequest"
31324         };
31325         
31326         for (var headerName in headers) {
31327             var headerValue = headers[headerName];
31328             if (headerValue) {
31329                 this.xhr.setRequestHeader(headerName, headerValue);
31330             }
31331         }
31332         
31333         var _this = this;
31334         
31335         this.xhr.onload = function()
31336         {
31337             _this.xhrOnLoad(_this.xhr);
31338         }
31339         
31340         this.xhr.onerror = function()
31341         {
31342             _this.xhrOnError(_this.xhr);
31343         }
31344         
31345         var formData = new FormData();
31346
31347         formData.append('returnHTML', 'NO');
31348         
31349         if(crop){
31350             formData.append('crop', crop);
31351         }
31352         
31353         formData.append(this.paramName, file, file.name);
31354         
31355         var options = {
31356             file : file, 
31357             manually : false
31358         };
31359         
31360         if(this.fireEvent('prepare', this, formData, options) != false){
31361             
31362             if(options.manually){
31363                 return;
31364             }
31365             
31366             this.xhr.send(formData);
31367             return;
31368         };
31369         
31370         this.uploadCancel();
31371     },
31372     
31373     uploadCancel : function()
31374     {
31375         if (this.xhr) {
31376             this.xhr.abort();
31377         }
31378         
31379         this.delegates = [];
31380         
31381         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31382             el.remove();
31383         }, this);
31384         
31385         this.arrange();
31386     },
31387     
31388     renderPreview : function(file)
31389     {
31390         if(typeof(file.target) != 'undefined' && file.target){
31391             return file;
31392         }
31393         
31394         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31395         
31396         var previewEl = this.managerEl.createChild({
31397             tag : 'div',
31398             cls : 'roo-document-manager-preview',
31399             cn : [
31400                 {
31401                     tag : 'div',
31402                     tooltip : file[this.toolTipName],
31403                     cls : 'roo-document-manager-thumb',
31404                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31405                 },
31406                 {
31407                     tag : 'button',
31408                     cls : 'close',
31409                     html : '<i class="fa fa-times-circle"></i>'
31410                 }
31411             ]
31412         });
31413
31414         var close = previewEl.select('button.close', true).first();
31415
31416         close.on('click', this.onRemove, this, file);
31417
31418         file.target = previewEl;
31419
31420         var image = previewEl.select('img', true).first();
31421         
31422         var _this = this;
31423         
31424         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31425         
31426         image.on('click', this.onClick, this, file);
31427         
31428         this.fireEvent('previewrendered', this, file);
31429         
31430         return file;
31431         
31432     },
31433     
31434     onPreviewLoad : function(file, image)
31435     {
31436         if(typeof(file.target) == 'undefined' || !file.target){
31437             return;
31438         }
31439         
31440         var width = image.dom.naturalWidth || image.dom.width;
31441         var height = image.dom.naturalHeight || image.dom.height;
31442         
31443         if(!this.previewResize) {
31444             return;
31445         }
31446         
31447         if(width > height){
31448             file.target.addClass('wide');
31449             return;
31450         }
31451         
31452         file.target.addClass('tall');
31453         return;
31454         
31455     },
31456     
31457     uploadFromSource : function(file, crop)
31458     {
31459         this.xhr = new XMLHttpRequest();
31460         
31461         this.managerEl.createChild({
31462             tag : 'div',
31463             cls : 'roo-document-manager-loading',
31464             cn : [
31465                 {
31466                     tag : 'div',
31467                     tooltip : file.name,
31468                     cls : 'roo-document-manager-thumb',
31469                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31470                 }
31471             ]
31472
31473         });
31474
31475         this.xhr.open(this.method, this.url, true);
31476         
31477         var headers = {
31478             "Accept": "application/json",
31479             "Cache-Control": "no-cache",
31480             "X-Requested-With": "XMLHttpRequest"
31481         };
31482         
31483         for (var headerName in headers) {
31484             var headerValue = headers[headerName];
31485             if (headerValue) {
31486                 this.xhr.setRequestHeader(headerName, headerValue);
31487             }
31488         }
31489         
31490         var _this = this;
31491         
31492         this.xhr.onload = function()
31493         {
31494             _this.xhrOnLoad(_this.xhr);
31495         }
31496         
31497         this.xhr.onerror = function()
31498         {
31499             _this.xhrOnError(_this.xhr);
31500         }
31501         
31502         var formData = new FormData();
31503
31504         formData.append('returnHTML', 'NO');
31505         
31506         formData.append('crop', crop);
31507         
31508         if(typeof(file.filename) != 'undefined'){
31509             formData.append('filename', file.filename);
31510         }
31511         
31512         if(typeof(file.mimetype) != 'undefined'){
31513             formData.append('mimetype', file.mimetype);
31514         }
31515         
31516         Roo.log(formData);
31517         
31518         if(this.fireEvent('prepare', this, formData) != false){
31519             this.xhr.send(formData);
31520         };
31521     }
31522 });
31523
31524 /*
31525 * Licence: LGPL
31526 */
31527
31528 /**
31529  * @class Roo.bootstrap.DocumentViewer
31530  * @extends Roo.bootstrap.Component
31531  * Bootstrap DocumentViewer class
31532  * @cfg {Boolean} showDownload (true|false) show download button (default true)
31533  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31534  * 
31535  * @constructor
31536  * Create a new DocumentViewer
31537  * @param {Object} config The config object
31538  */
31539
31540 Roo.bootstrap.DocumentViewer = function(config){
31541     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31542     
31543     this.addEvents({
31544         /**
31545          * @event initial
31546          * Fire after initEvent
31547          * @param {Roo.bootstrap.DocumentViewer} this
31548          */
31549         "initial" : true,
31550         /**
31551          * @event click
31552          * Fire after click
31553          * @param {Roo.bootstrap.DocumentViewer} this
31554          */
31555         "click" : true,
31556         /**
31557          * @event download
31558          * Fire after download button
31559          * @param {Roo.bootstrap.DocumentViewer} this
31560          */
31561         "download" : true,
31562         /**
31563          * @event trash
31564          * Fire after trash button
31565          * @param {Roo.bootstrap.DocumentViewer} this
31566          */
31567         "trash" : true
31568         
31569     });
31570 };
31571
31572 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
31573     
31574     showDownload : true,
31575     
31576     showTrash : true,
31577     
31578     getAutoCreate : function()
31579     {
31580         var cfg = {
31581             tag : 'div',
31582             cls : 'roo-document-viewer',
31583             cn : [
31584                 {
31585                     tag : 'div',
31586                     cls : 'roo-document-viewer-body',
31587                     cn : [
31588                         {
31589                             tag : 'div',
31590                             cls : 'roo-document-viewer-thumb',
31591                             cn : [
31592                                 {
31593                                     tag : 'img',
31594                                     cls : 'roo-document-viewer-image'
31595                                 }
31596                             ]
31597                         }
31598                     ]
31599                 },
31600                 {
31601                     tag : 'div',
31602                     cls : 'roo-document-viewer-footer',
31603                     cn : {
31604                         tag : 'div',
31605                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31606                         cn : [
31607                             {
31608                                 tag : 'div',
31609                                 cls : 'btn-group roo-document-viewer-download',
31610                                 cn : [
31611                                     {
31612                                         tag : 'button',
31613                                         cls : 'btn btn-default',
31614                                         html : '<i class="fa fa-download"></i>'
31615                                     }
31616                                 ]
31617                             },
31618                             {
31619                                 tag : 'div',
31620                                 cls : 'btn-group roo-document-viewer-trash',
31621                                 cn : [
31622                                     {
31623                                         tag : 'button',
31624                                         cls : 'btn btn-default',
31625                                         html : '<i class="fa fa-trash"></i>'
31626                                     }
31627                                 ]
31628                             }
31629                         ]
31630                     }
31631                 }
31632             ]
31633         };
31634         
31635         return cfg;
31636     },
31637     
31638     initEvents : function()
31639     {
31640         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31641         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31642         
31643         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31644         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31645         
31646         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31647         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31648         
31649         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31650         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31651         
31652         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31653         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31654         
31655         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31656         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31657         
31658         this.bodyEl.on('click', this.onClick, this);
31659         this.downloadBtn.on('click', this.onDownload, this);
31660         this.trashBtn.on('click', this.onTrash, this);
31661         
31662         this.downloadBtn.hide();
31663         this.trashBtn.hide();
31664         
31665         if(this.showDownload){
31666             this.downloadBtn.show();
31667         }
31668         
31669         if(this.showTrash){
31670             this.trashBtn.show();
31671         }
31672         
31673         if(!this.showDownload && !this.showTrash) {
31674             this.footerEl.hide();
31675         }
31676         
31677     },
31678     
31679     initial : function()
31680     {
31681         this.fireEvent('initial', this);
31682         
31683     },
31684     
31685     onClick : function(e)
31686     {
31687         e.preventDefault();
31688         
31689         this.fireEvent('click', this);
31690     },
31691     
31692     onDownload : function(e)
31693     {
31694         e.preventDefault();
31695         
31696         this.fireEvent('download', this);
31697     },
31698     
31699     onTrash : function(e)
31700     {
31701         e.preventDefault();
31702         
31703         this.fireEvent('trash', this);
31704     }
31705     
31706 });
31707 /*
31708  * - LGPL
31709  *
31710  * nav progress bar
31711  * 
31712  */
31713
31714 /**
31715  * @class Roo.bootstrap.NavProgressBar
31716  * @extends Roo.bootstrap.Component
31717  * Bootstrap NavProgressBar class
31718  * 
31719  * @constructor
31720  * Create a new nav progress bar
31721  * @param {Object} config The config object
31722  */
31723
31724 Roo.bootstrap.NavProgressBar = function(config){
31725     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31726
31727     this.bullets = this.bullets || [];
31728    
31729 //    Roo.bootstrap.NavProgressBar.register(this);
31730      this.addEvents({
31731         /**
31732              * @event changed
31733              * Fires when the active item changes
31734              * @param {Roo.bootstrap.NavProgressBar} this
31735              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31736              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
31737          */
31738         'changed': true
31739      });
31740     
31741 };
31742
31743 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
31744     
31745     bullets : [],
31746     barItems : [],
31747     
31748     getAutoCreate : function()
31749     {
31750         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31751         
31752         cfg = {
31753             tag : 'div',
31754             cls : 'roo-navigation-bar-group',
31755             cn : [
31756                 {
31757                     tag : 'div',
31758                     cls : 'roo-navigation-top-bar'
31759                 },
31760                 {
31761                     tag : 'div',
31762                     cls : 'roo-navigation-bullets-bar',
31763                     cn : [
31764                         {
31765                             tag : 'ul',
31766                             cls : 'roo-navigation-bar'
31767                         }
31768                     ]
31769                 },
31770                 
31771                 {
31772                     tag : 'div',
31773                     cls : 'roo-navigation-bottom-bar'
31774                 }
31775             ]
31776             
31777         };
31778         
31779         return cfg;
31780         
31781     },
31782     
31783     initEvents: function() 
31784     {
31785         
31786     },
31787     
31788     onRender : function(ct, position) 
31789     {
31790         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31791         
31792         if(this.bullets.length){
31793             Roo.each(this.bullets, function(b){
31794                this.addItem(b);
31795             }, this);
31796         }
31797         
31798         this.format();
31799         
31800     },
31801     
31802     addItem : function(cfg)
31803     {
31804         var item = new Roo.bootstrap.NavProgressItem(cfg);
31805         
31806         item.parentId = this.id;
31807         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
31808         
31809         if(cfg.html){
31810             var top = new Roo.bootstrap.Element({
31811                 tag : 'div',
31812                 cls : 'roo-navigation-bar-text'
31813             });
31814             
31815             var bottom = new Roo.bootstrap.Element({
31816                 tag : 'div',
31817                 cls : 'roo-navigation-bar-text'
31818             });
31819             
31820             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
31821             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
31822             
31823             var topText = new Roo.bootstrap.Element({
31824                 tag : 'span',
31825                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
31826             });
31827             
31828             var bottomText = new Roo.bootstrap.Element({
31829                 tag : 'span',
31830                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
31831             });
31832             
31833             topText.onRender(top.el, null);
31834             bottomText.onRender(bottom.el, null);
31835             
31836             item.topEl = top;
31837             item.bottomEl = bottom;
31838         }
31839         
31840         this.barItems.push(item);
31841         
31842         return item;
31843     },
31844     
31845     getActive : function()
31846     {
31847         var active = false;
31848         
31849         Roo.each(this.barItems, function(v){
31850             
31851             if (!v.isActive()) {
31852                 return;
31853             }
31854             
31855             active = v;
31856             return false;
31857             
31858         });
31859         
31860         return active;
31861     },
31862     
31863     setActiveItem : function(item)
31864     {
31865         var prev = false;
31866         
31867         Roo.each(this.barItems, function(v){
31868             if (v.rid == item.rid) {
31869                 return ;
31870             }
31871             
31872             if (v.isActive()) {
31873                 v.setActive(false);
31874                 prev = v;
31875             }
31876         });
31877
31878         item.setActive(true);
31879         
31880         this.fireEvent('changed', this, item, prev);
31881     },
31882     
31883     getBarItem: function(rid)
31884     {
31885         var ret = false;
31886         
31887         Roo.each(this.barItems, function(e) {
31888             if (e.rid != rid) {
31889                 return;
31890             }
31891             
31892             ret =  e;
31893             return false;
31894         });
31895         
31896         return ret;
31897     },
31898     
31899     indexOfItem : function(item)
31900     {
31901         var index = false;
31902         
31903         Roo.each(this.barItems, function(v, i){
31904             
31905             if (v.rid != item.rid) {
31906                 return;
31907             }
31908             
31909             index = i;
31910             return false
31911         });
31912         
31913         return index;
31914     },
31915     
31916     setActiveNext : function()
31917     {
31918         var i = this.indexOfItem(this.getActive());
31919         
31920         if (i > this.barItems.length) {
31921             return;
31922         }
31923         
31924         this.setActiveItem(this.barItems[i+1]);
31925     },
31926     
31927     setActivePrev : function()
31928     {
31929         var i = this.indexOfItem(this.getActive());
31930         
31931         if (i  < 1) {
31932             return;
31933         }
31934         
31935         this.setActiveItem(this.barItems[i-1]);
31936     },
31937     
31938     format : function()
31939     {
31940         if(!this.barItems.length){
31941             return;
31942         }
31943      
31944         var width = 100 / this.barItems.length;
31945         
31946         Roo.each(this.barItems, function(i){
31947             i.el.setStyle('width', width + '%');
31948             i.topEl.el.setStyle('width', width + '%');
31949             i.bottomEl.el.setStyle('width', width + '%');
31950         }, this);
31951         
31952     }
31953     
31954 });
31955 /*
31956  * - LGPL
31957  *
31958  * Nav Progress Item
31959  * 
31960  */
31961
31962 /**
31963  * @class Roo.bootstrap.NavProgressItem
31964  * @extends Roo.bootstrap.Component
31965  * Bootstrap NavProgressItem class
31966  * @cfg {String} rid the reference id
31967  * @cfg {Boolean} active (true|false) Is item active default false
31968  * @cfg {Boolean} disabled (true|false) Is item active default false
31969  * @cfg {String} html
31970  * @cfg {String} position (top|bottom) text position default bottom
31971  * @cfg {String} icon show icon instead of number
31972  * 
31973  * @constructor
31974  * Create a new NavProgressItem
31975  * @param {Object} config The config object
31976  */
31977 Roo.bootstrap.NavProgressItem = function(config){
31978     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
31979     this.addEvents({
31980         // raw events
31981         /**
31982          * @event click
31983          * The raw click event for the entire grid.
31984          * @param {Roo.bootstrap.NavProgressItem} this
31985          * @param {Roo.EventObject} e
31986          */
31987         "click" : true
31988     });
31989    
31990 };
31991
31992 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
31993     
31994     rid : '',
31995     active : false,
31996     disabled : false,
31997     html : '',
31998     position : 'bottom',
31999     icon : false,
32000     
32001     getAutoCreate : function()
32002     {
32003         var iconCls = 'roo-navigation-bar-item-icon';
32004         
32005         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32006         
32007         var cfg = {
32008             tag: 'li',
32009             cls: 'roo-navigation-bar-item',
32010             cn : [
32011                 {
32012                     tag : 'i',
32013                     cls : iconCls
32014                 }
32015             ]
32016         };
32017         
32018         if(this.active){
32019             cfg.cls += ' active';
32020         }
32021         if(this.disabled){
32022             cfg.cls += ' disabled';
32023         }
32024         
32025         return cfg;
32026     },
32027     
32028     disable : function()
32029     {
32030         this.setDisabled(true);
32031     },
32032     
32033     enable : function()
32034     {
32035         this.setDisabled(false);
32036     },
32037     
32038     initEvents: function() 
32039     {
32040         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32041         
32042         this.iconEl.on('click', this.onClick, this);
32043     },
32044     
32045     onClick : function(e)
32046     {
32047         e.preventDefault();
32048         
32049         if(this.disabled){
32050             return;
32051         }
32052         
32053         if(this.fireEvent('click', this, e) === false){
32054             return;
32055         };
32056         
32057         this.parent().setActiveItem(this);
32058     },
32059     
32060     isActive: function () 
32061     {
32062         return this.active;
32063     },
32064     
32065     setActive : function(state)
32066     {
32067         if(this.active == state){
32068             return;
32069         }
32070         
32071         this.active = state;
32072         
32073         if (state) {
32074             this.el.addClass('active');
32075             return;
32076         }
32077         
32078         this.el.removeClass('active');
32079         
32080         return;
32081     },
32082     
32083     setDisabled : function(state)
32084     {
32085         if(this.disabled == state){
32086             return;
32087         }
32088         
32089         this.disabled = state;
32090         
32091         if (state) {
32092             this.el.addClass('disabled');
32093             return;
32094         }
32095         
32096         this.el.removeClass('disabled');
32097     },
32098     
32099     tooltipEl : function()
32100     {
32101         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32102     }
32103 });
32104  
32105
32106  /*
32107  * - LGPL
32108  *
32109  * FieldLabel
32110  * 
32111  */
32112
32113 /**
32114  * @class Roo.bootstrap.FieldLabel
32115  * @extends Roo.bootstrap.Component
32116  * Bootstrap FieldLabel class
32117  * @cfg {String} html contents of the element
32118  * @cfg {String} tag tag of the element default label
32119  * @cfg {String} cls class of the element
32120  * @cfg {String} target label target 
32121  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32122  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32123  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32124  * @cfg {String} iconTooltip default "This field is required"
32125  * @cfg {String} indicatorpos (left|right) default left
32126  * 
32127  * @constructor
32128  * Create a new FieldLabel
32129  * @param {Object} config The config object
32130  */
32131
32132 Roo.bootstrap.FieldLabel = function(config){
32133     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32134     
32135     this.addEvents({
32136             /**
32137              * @event invalid
32138              * Fires after the field has been marked as invalid.
32139              * @param {Roo.form.FieldLabel} this
32140              * @param {String} msg The validation message
32141              */
32142             invalid : true,
32143             /**
32144              * @event valid
32145              * Fires after the field has been validated with no errors.
32146              * @param {Roo.form.FieldLabel} this
32147              */
32148             valid : true
32149         });
32150 };
32151
32152 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32153     
32154     tag: 'label',
32155     cls: '',
32156     html: '',
32157     target: '',
32158     allowBlank : true,
32159     invalidClass : 'has-warning',
32160     validClass : 'has-success',
32161     iconTooltip : 'This field is required',
32162     indicatorpos : 'left',
32163     
32164     getAutoCreate : function(){
32165         
32166         var cls = "";
32167         if (!this.allowBlank) {
32168             cls  = "visible";
32169         }
32170         
32171         var cfg = {
32172             tag : this.tag,
32173             cls : 'roo-bootstrap-field-label ' + this.cls,
32174             for : this.target,
32175             cn : [
32176                 {
32177                     tag : 'i',
32178                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32179                     tooltip : this.iconTooltip
32180                 },
32181                 {
32182                     tag : 'span',
32183                     html : this.html
32184                 }
32185             ] 
32186         };
32187         
32188         if(this.indicatorpos == 'right'){
32189             var cfg = {
32190                 tag : this.tag,
32191                 cls : 'roo-bootstrap-field-label ' + this.cls,
32192                 for : this.target,
32193                 cn : [
32194                     {
32195                         tag : 'span',
32196                         html : this.html
32197                     },
32198                     {
32199                         tag : 'i',
32200                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32201                         tooltip : this.iconTooltip
32202                     }
32203                 ] 
32204             };
32205         }
32206         
32207         return cfg;
32208     },
32209     
32210     initEvents: function() 
32211     {
32212         Roo.bootstrap.Element.superclass.initEvents.call(this);
32213         
32214         this.indicator = this.indicatorEl();
32215         
32216         if(this.indicator){
32217             this.indicator.removeClass('visible');
32218             this.indicator.addClass('invisible');
32219         }
32220         
32221         Roo.bootstrap.FieldLabel.register(this);
32222     },
32223     
32224     indicatorEl : function()
32225     {
32226         var indicator = this.el.select('i.roo-required-indicator',true).first();
32227         
32228         if(!indicator){
32229             return false;
32230         }
32231         
32232         return indicator;
32233         
32234     },
32235     
32236     /**
32237      * Mark this field as valid
32238      */
32239     markValid : function()
32240     {
32241         if(this.indicator){
32242             this.indicator.removeClass('visible');
32243             this.indicator.addClass('invisible');
32244         }
32245         if (Roo.bootstrap.version == 3) {
32246             this.el.removeClass(this.invalidClass);
32247             this.el.addClass(this.validClass);
32248         } else {
32249             this.el.removeClass('is-invalid');
32250             this.el.addClass('is-valid');
32251         }
32252         
32253         
32254         this.fireEvent('valid', this);
32255     },
32256     
32257     /**
32258      * Mark this field as invalid
32259      * @param {String} msg The validation message
32260      */
32261     markInvalid : function(msg)
32262     {
32263         if(this.indicator){
32264             this.indicator.removeClass('invisible');
32265             this.indicator.addClass('visible');
32266         }
32267           if (Roo.bootstrap.version == 3) {
32268             this.el.removeClass(this.validClass);
32269             this.el.addClass(this.invalidClass);
32270         } else {
32271             this.el.removeClass('is-valid');
32272             this.el.addClass('is-invalid');
32273         }
32274         
32275         
32276         this.fireEvent('invalid', this, msg);
32277     }
32278     
32279    
32280 });
32281
32282 Roo.apply(Roo.bootstrap.FieldLabel, {
32283     
32284     groups: {},
32285     
32286      /**
32287     * register a FieldLabel Group
32288     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32289     */
32290     register : function(label)
32291     {
32292         if(this.groups.hasOwnProperty(label.target)){
32293             return;
32294         }
32295      
32296         this.groups[label.target] = label;
32297         
32298     },
32299     /**
32300     * fetch a FieldLabel Group based on the target
32301     * @param {string} target
32302     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32303     */
32304     get: function(target) {
32305         if (typeof(this.groups[target]) == 'undefined') {
32306             return false;
32307         }
32308         
32309         return this.groups[target] ;
32310     }
32311 });
32312
32313  
32314
32315  /*
32316  * - LGPL
32317  *
32318  * page DateSplitField.
32319  * 
32320  */
32321
32322
32323 /**
32324  * @class Roo.bootstrap.DateSplitField
32325  * @extends Roo.bootstrap.Component
32326  * Bootstrap DateSplitField class
32327  * @cfg {string} fieldLabel - the label associated
32328  * @cfg {Number} labelWidth set the width of label (0-12)
32329  * @cfg {String} labelAlign (top|left)
32330  * @cfg {Boolean} dayAllowBlank (true|false) default false
32331  * @cfg {Boolean} monthAllowBlank (true|false) default false
32332  * @cfg {Boolean} yearAllowBlank (true|false) default false
32333  * @cfg {string} dayPlaceholder 
32334  * @cfg {string} monthPlaceholder
32335  * @cfg {string} yearPlaceholder
32336  * @cfg {string} dayFormat default 'd'
32337  * @cfg {string} monthFormat default 'm'
32338  * @cfg {string} yearFormat default 'Y'
32339  * @cfg {Number} labellg set the width of label (1-12)
32340  * @cfg {Number} labelmd set the width of label (1-12)
32341  * @cfg {Number} labelsm set the width of label (1-12)
32342  * @cfg {Number} labelxs set the width of label (1-12)
32343
32344  *     
32345  * @constructor
32346  * Create a new DateSplitField
32347  * @param {Object} config The config object
32348  */
32349
32350 Roo.bootstrap.DateSplitField = function(config){
32351     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32352     
32353     this.addEvents({
32354         // raw events
32355          /**
32356          * @event years
32357          * getting the data of years
32358          * @param {Roo.bootstrap.DateSplitField} this
32359          * @param {Object} years
32360          */
32361         "years" : true,
32362         /**
32363          * @event days
32364          * getting the data of days
32365          * @param {Roo.bootstrap.DateSplitField} this
32366          * @param {Object} days
32367          */
32368         "days" : true,
32369         /**
32370          * @event invalid
32371          * Fires after the field has been marked as invalid.
32372          * @param {Roo.form.Field} this
32373          * @param {String} msg The validation message
32374          */
32375         invalid : true,
32376        /**
32377          * @event valid
32378          * Fires after the field has been validated with no errors.
32379          * @param {Roo.form.Field} this
32380          */
32381         valid : true
32382     });
32383 };
32384
32385 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32386     
32387     fieldLabel : '',
32388     labelAlign : 'top',
32389     labelWidth : 3,
32390     dayAllowBlank : false,
32391     monthAllowBlank : false,
32392     yearAllowBlank : false,
32393     dayPlaceholder : '',
32394     monthPlaceholder : '',
32395     yearPlaceholder : '',
32396     dayFormat : 'd',
32397     monthFormat : 'm',
32398     yearFormat : 'Y',
32399     isFormField : true,
32400     labellg : 0,
32401     labelmd : 0,
32402     labelsm : 0,
32403     labelxs : 0,
32404     
32405     getAutoCreate : function()
32406     {
32407         var cfg = {
32408             tag : 'div',
32409             cls : 'row roo-date-split-field-group',
32410             cn : [
32411                 {
32412                     tag : 'input',
32413                     type : 'hidden',
32414                     cls : 'form-hidden-field roo-date-split-field-group-value',
32415                     name : this.name
32416                 }
32417             ]
32418         };
32419         
32420         var labelCls = 'col-md-12';
32421         var contentCls = 'col-md-4';
32422         
32423         if(this.fieldLabel){
32424             
32425             var label = {
32426                 tag : 'div',
32427                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32428                 cn : [
32429                     {
32430                         tag : 'label',
32431                         html : this.fieldLabel
32432                     }
32433                 ]
32434             };
32435             
32436             if(this.labelAlign == 'left'){
32437             
32438                 if(this.labelWidth > 12){
32439                     label.style = "width: " + this.labelWidth + 'px';
32440                 }
32441
32442                 if(this.labelWidth < 13 && this.labelmd == 0){
32443                     this.labelmd = this.labelWidth;
32444                 }
32445
32446                 if(this.labellg > 0){
32447                     labelCls = ' col-lg-' + this.labellg;
32448                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32449                 }
32450
32451                 if(this.labelmd > 0){
32452                     labelCls = ' col-md-' + this.labelmd;
32453                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32454                 }
32455
32456                 if(this.labelsm > 0){
32457                     labelCls = ' col-sm-' + this.labelsm;
32458                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32459                 }
32460
32461                 if(this.labelxs > 0){
32462                     labelCls = ' col-xs-' + this.labelxs;
32463                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32464                 }
32465             }
32466             
32467             label.cls += ' ' + labelCls;
32468             
32469             cfg.cn.push(label);
32470         }
32471         
32472         Roo.each(['day', 'month', 'year'], function(t){
32473             cfg.cn.push({
32474                 tag : 'div',
32475                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32476             });
32477         }, this);
32478         
32479         return cfg;
32480     },
32481     
32482     inputEl: function ()
32483     {
32484         return this.el.select('.roo-date-split-field-group-value', true).first();
32485     },
32486     
32487     onRender : function(ct, position) 
32488     {
32489         var _this = this;
32490         
32491         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32492         
32493         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32494         
32495         this.dayField = new Roo.bootstrap.ComboBox({
32496             allowBlank : this.dayAllowBlank,
32497             alwaysQuery : true,
32498             displayField : 'value',
32499             editable : false,
32500             fieldLabel : '',
32501             forceSelection : true,
32502             mode : 'local',
32503             placeholder : this.dayPlaceholder,
32504             selectOnFocus : true,
32505             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32506             triggerAction : 'all',
32507             typeAhead : true,
32508             valueField : 'value',
32509             store : new Roo.data.SimpleStore({
32510                 data : (function() {    
32511                     var days = [];
32512                     _this.fireEvent('days', _this, days);
32513                     return days;
32514                 })(),
32515                 fields : [ 'value' ]
32516             }),
32517             listeners : {
32518                 select : function (_self, record, index)
32519                 {
32520                     _this.setValue(_this.getValue());
32521                 }
32522             }
32523         });
32524
32525         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32526         
32527         this.monthField = new Roo.bootstrap.MonthField({
32528             after : '<i class=\"fa fa-calendar\"></i>',
32529             allowBlank : this.monthAllowBlank,
32530             placeholder : this.monthPlaceholder,
32531             readOnly : true,
32532             listeners : {
32533                 render : function (_self)
32534                 {
32535                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
32536                         e.preventDefault();
32537                         _self.focus();
32538                     });
32539                 },
32540                 select : function (_self, oldvalue, newvalue)
32541                 {
32542                     _this.setValue(_this.getValue());
32543                 }
32544             }
32545         });
32546         
32547         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32548         
32549         this.yearField = new Roo.bootstrap.ComboBox({
32550             allowBlank : this.yearAllowBlank,
32551             alwaysQuery : true,
32552             displayField : 'value',
32553             editable : false,
32554             fieldLabel : '',
32555             forceSelection : true,
32556             mode : 'local',
32557             placeholder : this.yearPlaceholder,
32558             selectOnFocus : true,
32559             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32560             triggerAction : 'all',
32561             typeAhead : true,
32562             valueField : 'value',
32563             store : new Roo.data.SimpleStore({
32564                 data : (function() {
32565                     var years = [];
32566                     _this.fireEvent('years', _this, years);
32567                     return years;
32568                 })(),
32569                 fields : [ 'value' ]
32570             }),
32571             listeners : {
32572                 select : function (_self, record, index)
32573                 {
32574                     _this.setValue(_this.getValue());
32575                 }
32576             }
32577         });
32578
32579         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32580     },
32581     
32582     setValue : function(v, format)
32583     {
32584         this.inputEl.dom.value = v;
32585         
32586         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32587         
32588         var d = Date.parseDate(v, f);
32589         
32590         if(!d){
32591             this.validate();
32592             return;
32593         }
32594         
32595         this.setDay(d.format(this.dayFormat));
32596         this.setMonth(d.format(this.monthFormat));
32597         this.setYear(d.format(this.yearFormat));
32598         
32599         this.validate();
32600         
32601         return;
32602     },
32603     
32604     setDay : function(v)
32605     {
32606         this.dayField.setValue(v);
32607         this.inputEl.dom.value = this.getValue();
32608         this.validate();
32609         return;
32610     },
32611     
32612     setMonth : function(v)
32613     {
32614         this.monthField.setValue(v, true);
32615         this.inputEl.dom.value = this.getValue();
32616         this.validate();
32617         return;
32618     },
32619     
32620     setYear : function(v)
32621     {
32622         this.yearField.setValue(v);
32623         this.inputEl.dom.value = this.getValue();
32624         this.validate();
32625         return;
32626     },
32627     
32628     getDay : function()
32629     {
32630         return this.dayField.getValue();
32631     },
32632     
32633     getMonth : function()
32634     {
32635         return this.monthField.getValue();
32636     },
32637     
32638     getYear : function()
32639     {
32640         return this.yearField.getValue();
32641     },
32642     
32643     getValue : function()
32644     {
32645         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32646         
32647         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32648         
32649         return date;
32650     },
32651     
32652     reset : function()
32653     {
32654         this.setDay('');
32655         this.setMonth('');
32656         this.setYear('');
32657         this.inputEl.dom.value = '';
32658         this.validate();
32659         return;
32660     },
32661     
32662     validate : function()
32663     {
32664         var d = this.dayField.validate();
32665         var m = this.monthField.validate();
32666         var y = this.yearField.validate();
32667         
32668         var valid = true;
32669         
32670         if(
32671                 (!this.dayAllowBlank && !d) ||
32672                 (!this.monthAllowBlank && !m) ||
32673                 (!this.yearAllowBlank && !y)
32674         ){
32675             valid = false;
32676         }
32677         
32678         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32679             return valid;
32680         }
32681         
32682         if(valid){
32683             this.markValid();
32684             return valid;
32685         }
32686         
32687         this.markInvalid();
32688         
32689         return valid;
32690     },
32691     
32692     markValid : function()
32693     {
32694         
32695         var label = this.el.select('label', true).first();
32696         var icon = this.el.select('i.fa-star', true).first();
32697
32698         if(label && icon){
32699             icon.remove();
32700         }
32701         
32702         this.fireEvent('valid', this);
32703     },
32704     
32705      /**
32706      * Mark this field as invalid
32707      * @param {String} msg The validation message
32708      */
32709     markInvalid : function(msg)
32710     {
32711         
32712         var label = this.el.select('label', true).first();
32713         var icon = this.el.select('i.fa-star', true).first();
32714
32715         if(label && !icon){
32716             this.el.select('.roo-date-split-field-label', true).createChild({
32717                 tag : 'i',
32718                 cls : 'text-danger fa fa-lg fa-star',
32719                 tooltip : 'This field is required',
32720                 style : 'margin-right:5px;'
32721             }, label, true);
32722         }
32723         
32724         this.fireEvent('invalid', this, msg);
32725     },
32726     
32727     clearInvalid : function()
32728     {
32729         var label = this.el.select('label', true).first();
32730         var icon = this.el.select('i.fa-star', true).first();
32731
32732         if(label && icon){
32733             icon.remove();
32734         }
32735         
32736         this.fireEvent('valid', this);
32737     },
32738     
32739     getName: function()
32740     {
32741         return this.name;
32742     }
32743     
32744 });
32745
32746  /**
32747  *
32748  * This is based on 
32749  * http://masonry.desandro.com
32750  *
32751  * The idea is to render all the bricks based on vertical width...
32752  *
32753  * The original code extends 'outlayer' - we might need to use that....
32754  * 
32755  */
32756
32757
32758 /**
32759  * @class Roo.bootstrap.LayoutMasonry
32760  * @extends Roo.bootstrap.Component
32761  * Bootstrap Layout Masonry class
32762  * 
32763  * @constructor
32764  * Create a new Element
32765  * @param {Object} config The config object
32766  */
32767
32768 Roo.bootstrap.LayoutMasonry = function(config){
32769     
32770     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32771     
32772     this.bricks = [];
32773     
32774     Roo.bootstrap.LayoutMasonry.register(this);
32775     
32776     this.addEvents({
32777         // raw events
32778         /**
32779          * @event layout
32780          * Fire after layout the items
32781          * @param {Roo.bootstrap.LayoutMasonry} this
32782          * @param {Roo.EventObject} e
32783          */
32784         "layout" : true
32785     });
32786     
32787 };
32788
32789 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
32790     
32791     /**
32792      * @cfg {Boolean} isLayoutInstant = no animation?
32793      */   
32794     isLayoutInstant : false, // needed?
32795    
32796     /**
32797      * @cfg {Number} boxWidth  width of the columns
32798      */   
32799     boxWidth : 450,
32800     
32801       /**
32802      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
32803      */   
32804     boxHeight : 0,
32805     
32806     /**
32807      * @cfg {Number} padWidth padding below box..
32808      */   
32809     padWidth : 10, 
32810     
32811     /**
32812      * @cfg {Number} gutter gutter width..
32813      */   
32814     gutter : 10,
32815     
32816      /**
32817      * @cfg {Number} maxCols maximum number of columns
32818      */   
32819     
32820     maxCols: 0,
32821     
32822     /**
32823      * @cfg {Boolean} isAutoInitial defalut true
32824      */   
32825     isAutoInitial : true, 
32826     
32827     containerWidth: 0,
32828     
32829     /**
32830      * @cfg {Boolean} isHorizontal defalut false
32831      */   
32832     isHorizontal : false, 
32833
32834     currentSize : null,
32835     
32836     tag: 'div',
32837     
32838     cls: '',
32839     
32840     bricks: null, //CompositeElement
32841     
32842     cols : 1,
32843     
32844     _isLayoutInited : false,
32845     
32846 //    isAlternative : false, // only use for vertical layout...
32847     
32848     /**
32849      * @cfg {Number} alternativePadWidth padding below box..
32850      */   
32851     alternativePadWidth : 50,
32852     
32853     selectedBrick : [],
32854     
32855     getAutoCreate : function(){
32856         
32857         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
32858         
32859         var cfg = {
32860             tag: this.tag,
32861             cls: 'blog-masonary-wrapper ' + this.cls,
32862             cn : {
32863                 cls : 'mas-boxes masonary'
32864             }
32865         };
32866         
32867         return cfg;
32868     },
32869     
32870     getChildContainer: function( )
32871     {
32872         if (this.boxesEl) {
32873             return this.boxesEl;
32874         }
32875         
32876         this.boxesEl = this.el.select('.mas-boxes').first();
32877         
32878         return this.boxesEl;
32879     },
32880     
32881     
32882     initEvents : function()
32883     {
32884         var _this = this;
32885         
32886         if(this.isAutoInitial){
32887             Roo.log('hook children rendered');
32888             this.on('childrenrendered', function() {
32889                 Roo.log('children rendered');
32890                 _this.initial();
32891             } ,this);
32892         }
32893     },
32894     
32895     initial : function()
32896     {
32897         this.selectedBrick = [];
32898         
32899         this.currentSize = this.el.getBox(true);
32900         
32901         Roo.EventManager.onWindowResize(this.resize, this); 
32902
32903         if(!this.isAutoInitial){
32904             this.layout();
32905             return;
32906         }
32907         
32908         this.layout();
32909         
32910         return;
32911         //this.layout.defer(500,this);
32912         
32913     },
32914     
32915     resize : function()
32916     {
32917         var cs = this.el.getBox(true);
32918         
32919         if (
32920                 this.currentSize.width == cs.width && 
32921                 this.currentSize.x == cs.x && 
32922                 this.currentSize.height == cs.height && 
32923                 this.currentSize.y == cs.y 
32924         ) {
32925             Roo.log("no change in with or X or Y");
32926             return;
32927         }
32928         
32929         this.currentSize = cs;
32930         
32931         this.layout();
32932         
32933     },
32934     
32935     layout : function()
32936     {   
32937         this._resetLayout();
32938         
32939         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32940         
32941         this.layoutItems( isInstant );
32942       
32943         this._isLayoutInited = true;
32944         
32945         this.fireEvent('layout', this);
32946         
32947     },
32948     
32949     _resetLayout : function()
32950     {
32951         if(this.isHorizontal){
32952             this.horizontalMeasureColumns();
32953             return;
32954         }
32955         
32956         this.verticalMeasureColumns();
32957         
32958     },
32959     
32960     verticalMeasureColumns : function()
32961     {
32962         this.getContainerWidth();
32963         
32964 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
32965 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
32966 //            return;
32967 //        }
32968         
32969         var boxWidth = this.boxWidth + this.padWidth;
32970         
32971         if(this.containerWidth < this.boxWidth){
32972             boxWidth = this.containerWidth
32973         }
32974         
32975         var containerWidth = this.containerWidth;
32976         
32977         var cols = Math.floor(containerWidth / boxWidth);
32978         
32979         this.cols = Math.max( cols, 1 );
32980         
32981         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32982         
32983         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
32984         
32985         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
32986         
32987         this.colWidth = boxWidth + avail - this.padWidth;
32988         
32989         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
32990         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
32991     },
32992     
32993     horizontalMeasureColumns : function()
32994     {
32995         this.getContainerWidth();
32996         
32997         var boxWidth = this.boxWidth;
32998         
32999         if(this.containerWidth < boxWidth){
33000             boxWidth = this.containerWidth;
33001         }
33002         
33003         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33004         
33005         this.el.setHeight(boxWidth);
33006         
33007     },
33008     
33009     getContainerWidth : function()
33010     {
33011         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33012     },
33013     
33014     layoutItems : function( isInstant )
33015     {
33016         Roo.log(this.bricks);
33017         
33018         var items = Roo.apply([], this.bricks);
33019         
33020         if(this.isHorizontal){
33021             this._horizontalLayoutItems( items , isInstant );
33022             return;
33023         }
33024         
33025 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33026 //            this._verticalAlternativeLayoutItems( items , isInstant );
33027 //            return;
33028 //        }
33029         
33030         this._verticalLayoutItems( items , isInstant );
33031         
33032     },
33033     
33034     _verticalLayoutItems : function ( items , isInstant)
33035     {
33036         if ( !items || !items.length ) {
33037             return;
33038         }
33039         
33040         var standard = [
33041             ['xs', 'xs', 'xs', 'tall'],
33042             ['xs', 'xs', 'tall'],
33043             ['xs', 'xs', 'sm'],
33044             ['xs', 'xs', 'xs'],
33045             ['xs', 'tall'],
33046             ['xs', 'sm'],
33047             ['xs', 'xs'],
33048             ['xs'],
33049             
33050             ['sm', 'xs', 'xs'],
33051             ['sm', 'xs'],
33052             ['sm'],
33053             
33054             ['tall', 'xs', 'xs', 'xs'],
33055             ['tall', 'xs', 'xs'],
33056             ['tall', 'xs'],
33057             ['tall']
33058             
33059         ];
33060         
33061         var queue = [];
33062         
33063         var boxes = [];
33064         
33065         var box = [];
33066         
33067         Roo.each(items, function(item, k){
33068             
33069             switch (item.size) {
33070                 // these layouts take up a full box,
33071                 case 'md' :
33072                 case 'md-left' :
33073                 case 'md-right' :
33074                 case 'wide' :
33075                     
33076                     if(box.length){
33077                         boxes.push(box);
33078                         box = [];
33079                     }
33080                     
33081                     boxes.push([item]);
33082                     
33083                     break;
33084                     
33085                 case 'xs' :
33086                 case 'sm' :
33087                 case 'tall' :
33088                     
33089                     box.push(item);
33090                     
33091                     break;
33092                 default :
33093                     break;
33094                     
33095             }
33096             
33097         }, this);
33098         
33099         if(box.length){
33100             boxes.push(box);
33101             box = [];
33102         }
33103         
33104         var filterPattern = function(box, length)
33105         {
33106             if(!box.length){
33107                 return;
33108             }
33109             
33110             var match = false;
33111             
33112             var pattern = box.slice(0, length);
33113             
33114             var format = [];
33115             
33116             Roo.each(pattern, function(i){
33117                 format.push(i.size);
33118             }, this);
33119             
33120             Roo.each(standard, function(s){
33121                 
33122                 if(String(s) != String(format)){
33123                     return;
33124                 }
33125                 
33126                 match = true;
33127                 return false;
33128                 
33129             }, this);
33130             
33131             if(!match && length == 1){
33132                 return;
33133             }
33134             
33135             if(!match){
33136                 filterPattern(box, length - 1);
33137                 return;
33138             }
33139                 
33140             queue.push(pattern);
33141
33142             box = box.slice(length, box.length);
33143
33144             filterPattern(box, 4);
33145
33146             return;
33147             
33148         }
33149         
33150         Roo.each(boxes, function(box, k){
33151             
33152             if(!box.length){
33153                 return;
33154             }
33155             
33156             if(box.length == 1){
33157                 queue.push(box);
33158                 return;
33159             }
33160             
33161             filterPattern(box, 4);
33162             
33163         }, this);
33164         
33165         this._processVerticalLayoutQueue( queue, isInstant );
33166         
33167     },
33168     
33169 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33170 //    {
33171 //        if ( !items || !items.length ) {
33172 //            return;
33173 //        }
33174 //
33175 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33176 //        
33177 //    },
33178     
33179     _horizontalLayoutItems : function ( items , isInstant)
33180     {
33181         if ( !items || !items.length || items.length < 3) {
33182             return;
33183         }
33184         
33185         items.reverse();
33186         
33187         var eItems = items.slice(0, 3);
33188         
33189         items = items.slice(3, items.length);
33190         
33191         var standard = [
33192             ['xs', 'xs', 'xs', 'wide'],
33193             ['xs', 'xs', 'wide'],
33194             ['xs', 'xs', 'sm'],
33195             ['xs', 'xs', 'xs'],
33196             ['xs', 'wide'],
33197             ['xs', 'sm'],
33198             ['xs', 'xs'],
33199             ['xs'],
33200             
33201             ['sm', 'xs', 'xs'],
33202             ['sm', 'xs'],
33203             ['sm'],
33204             
33205             ['wide', 'xs', 'xs', 'xs'],
33206             ['wide', 'xs', 'xs'],
33207             ['wide', 'xs'],
33208             ['wide'],
33209             
33210             ['wide-thin']
33211         ];
33212         
33213         var queue = [];
33214         
33215         var boxes = [];
33216         
33217         var box = [];
33218         
33219         Roo.each(items, function(item, k){
33220             
33221             switch (item.size) {
33222                 case 'md' :
33223                 case 'md-left' :
33224                 case 'md-right' :
33225                 case 'tall' :
33226                     
33227                     if(box.length){
33228                         boxes.push(box);
33229                         box = [];
33230                     }
33231                     
33232                     boxes.push([item]);
33233                     
33234                     break;
33235                     
33236                 case 'xs' :
33237                 case 'sm' :
33238                 case 'wide' :
33239                 case 'wide-thin' :
33240                     
33241                     box.push(item);
33242                     
33243                     break;
33244                 default :
33245                     break;
33246                     
33247             }
33248             
33249         }, this);
33250         
33251         if(box.length){
33252             boxes.push(box);
33253             box = [];
33254         }
33255         
33256         var filterPattern = function(box, length)
33257         {
33258             if(!box.length){
33259                 return;
33260             }
33261             
33262             var match = false;
33263             
33264             var pattern = box.slice(0, length);
33265             
33266             var format = [];
33267             
33268             Roo.each(pattern, function(i){
33269                 format.push(i.size);
33270             }, this);
33271             
33272             Roo.each(standard, function(s){
33273                 
33274                 if(String(s) != String(format)){
33275                     return;
33276                 }
33277                 
33278                 match = true;
33279                 return false;
33280                 
33281             }, this);
33282             
33283             if(!match && length == 1){
33284                 return;
33285             }
33286             
33287             if(!match){
33288                 filterPattern(box, length - 1);
33289                 return;
33290             }
33291                 
33292             queue.push(pattern);
33293
33294             box = box.slice(length, box.length);
33295
33296             filterPattern(box, 4);
33297
33298             return;
33299             
33300         }
33301         
33302         Roo.each(boxes, function(box, k){
33303             
33304             if(!box.length){
33305                 return;
33306             }
33307             
33308             if(box.length == 1){
33309                 queue.push(box);
33310                 return;
33311             }
33312             
33313             filterPattern(box, 4);
33314             
33315         }, this);
33316         
33317         
33318         var prune = [];
33319         
33320         var pos = this.el.getBox(true);
33321         
33322         var minX = pos.x;
33323         
33324         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33325         
33326         var hit_end = false;
33327         
33328         Roo.each(queue, function(box){
33329             
33330             if(hit_end){
33331                 
33332                 Roo.each(box, function(b){
33333                 
33334                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33335                     b.el.hide();
33336
33337                 }, this);
33338
33339                 return;
33340             }
33341             
33342             var mx = 0;
33343             
33344             Roo.each(box, function(b){
33345                 
33346                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33347                 b.el.show();
33348
33349                 mx = Math.max(mx, b.x);
33350                 
33351             }, this);
33352             
33353             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33354             
33355             if(maxX < minX){
33356                 
33357                 Roo.each(box, function(b){
33358                 
33359                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33360                     b.el.hide();
33361                     
33362                 }, this);
33363                 
33364                 hit_end = true;
33365                 
33366                 return;
33367             }
33368             
33369             prune.push(box);
33370             
33371         }, this);
33372         
33373         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33374     },
33375     
33376     /** Sets position of item in DOM
33377     * @param {Element} item
33378     * @param {Number} x - horizontal position
33379     * @param {Number} y - vertical position
33380     * @param {Boolean} isInstant - disables transitions
33381     */
33382     _processVerticalLayoutQueue : function( queue, isInstant )
33383     {
33384         var pos = this.el.getBox(true);
33385         var x = pos.x;
33386         var y = pos.y;
33387         var maxY = [];
33388         
33389         for (var i = 0; i < this.cols; i++){
33390             maxY[i] = pos.y;
33391         }
33392         
33393         Roo.each(queue, function(box, k){
33394             
33395             var col = k % this.cols;
33396             
33397             Roo.each(box, function(b,kk){
33398                 
33399                 b.el.position('absolute');
33400                 
33401                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33402                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33403                 
33404                 if(b.size == 'md-left' || b.size == 'md-right'){
33405                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33406                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33407                 }
33408                 
33409                 b.el.setWidth(width);
33410                 b.el.setHeight(height);
33411                 // iframe?
33412                 b.el.select('iframe',true).setSize(width,height);
33413                 
33414             }, this);
33415             
33416             for (var i = 0; i < this.cols; i++){
33417                 
33418                 if(maxY[i] < maxY[col]){
33419                     col = i;
33420                     continue;
33421                 }
33422                 
33423                 col = Math.min(col, i);
33424                 
33425             }
33426             
33427             x = pos.x + col * (this.colWidth + this.padWidth);
33428             
33429             y = maxY[col];
33430             
33431             var positions = [];
33432             
33433             switch (box.length){
33434                 case 1 :
33435                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33436                     break;
33437                 case 2 :
33438                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33439                     break;
33440                 case 3 :
33441                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33442                     break;
33443                 case 4 :
33444                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33445                     break;
33446                 default :
33447                     break;
33448             }
33449             
33450             Roo.each(box, function(b,kk){
33451                 
33452                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33453                 
33454                 var sz = b.el.getSize();
33455                 
33456                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33457                 
33458             }, this);
33459             
33460         }, this);
33461         
33462         var mY = 0;
33463         
33464         for (var i = 0; i < this.cols; i++){
33465             mY = Math.max(mY, maxY[i]);
33466         }
33467         
33468         this.el.setHeight(mY - pos.y);
33469         
33470     },
33471     
33472 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33473 //    {
33474 //        var pos = this.el.getBox(true);
33475 //        var x = pos.x;
33476 //        var y = pos.y;
33477 //        var maxX = pos.right;
33478 //        
33479 //        var maxHeight = 0;
33480 //        
33481 //        Roo.each(items, function(item, k){
33482 //            
33483 //            var c = k % 2;
33484 //            
33485 //            item.el.position('absolute');
33486 //                
33487 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33488 //
33489 //            item.el.setWidth(width);
33490 //
33491 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33492 //
33493 //            item.el.setHeight(height);
33494 //            
33495 //            if(c == 0){
33496 //                item.el.setXY([x, y], isInstant ? false : true);
33497 //            } else {
33498 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33499 //            }
33500 //            
33501 //            y = y + height + this.alternativePadWidth;
33502 //            
33503 //            maxHeight = maxHeight + height + this.alternativePadWidth;
33504 //            
33505 //        }, this);
33506 //        
33507 //        this.el.setHeight(maxHeight);
33508 //        
33509 //    },
33510     
33511     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33512     {
33513         var pos = this.el.getBox(true);
33514         
33515         var minX = pos.x;
33516         var minY = pos.y;
33517         
33518         var maxX = pos.right;
33519         
33520         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33521         
33522         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33523         
33524         Roo.each(queue, function(box, k){
33525             
33526             Roo.each(box, function(b, kk){
33527                 
33528                 b.el.position('absolute');
33529                 
33530                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33531                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33532                 
33533                 if(b.size == 'md-left' || b.size == 'md-right'){
33534                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33535                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33536                 }
33537                 
33538                 b.el.setWidth(width);
33539                 b.el.setHeight(height);
33540                 
33541             }, this);
33542             
33543             if(!box.length){
33544                 return;
33545             }
33546             
33547             var positions = [];
33548             
33549             switch (box.length){
33550                 case 1 :
33551                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33552                     break;
33553                 case 2 :
33554                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33555                     break;
33556                 case 3 :
33557                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33558                     break;
33559                 case 4 :
33560                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33561                     break;
33562                 default :
33563                     break;
33564             }
33565             
33566             Roo.each(box, function(b,kk){
33567                 
33568                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33569                 
33570                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33571                 
33572             }, this);
33573             
33574         }, this);
33575         
33576     },
33577     
33578     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33579     {
33580         Roo.each(eItems, function(b,k){
33581             
33582             b.size = (k == 0) ? 'sm' : 'xs';
33583             b.x = (k == 0) ? 2 : 1;
33584             b.y = (k == 0) ? 2 : 1;
33585             
33586             b.el.position('absolute');
33587             
33588             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33589                 
33590             b.el.setWidth(width);
33591             
33592             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33593             
33594             b.el.setHeight(height);
33595             
33596         }, this);
33597
33598         var positions = [];
33599         
33600         positions.push({
33601             x : maxX - this.unitWidth * 2 - this.gutter,
33602             y : minY
33603         });
33604         
33605         positions.push({
33606             x : maxX - this.unitWidth,
33607             y : minY + (this.unitWidth + this.gutter) * 2
33608         });
33609         
33610         positions.push({
33611             x : maxX - this.unitWidth * 3 - this.gutter * 2,
33612             y : minY
33613         });
33614         
33615         Roo.each(eItems, function(b,k){
33616             
33617             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33618
33619         }, this);
33620         
33621     },
33622     
33623     getVerticalOneBoxColPositions : function(x, y, box)
33624     {
33625         var pos = [];
33626         
33627         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33628         
33629         if(box[0].size == 'md-left'){
33630             rand = 0;
33631         }
33632         
33633         if(box[0].size == 'md-right'){
33634             rand = 1;
33635         }
33636         
33637         pos.push({
33638             x : x + (this.unitWidth + this.gutter) * rand,
33639             y : y
33640         });
33641         
33642         return pos;
33643     },
33644     
33645     getVerticalTwoBoxColPositions : function(x, y, box)
33646     {
33647         var pos = [];
33648         
33649         if(box[0].size == 'xs'){
33650             
33651             pos.push({
33652                 x : x,
33653                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33654             });
33655
33656             pos.push({
33657                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33658                 y : y
33659             });
33660             
33661             return pos;
33662             
33663         }
33664         
33665         pos.push({
33666             x : x,
33667             y : y
33668         });
33669
33670         pos.push({
33671             x : x + (this.unitWidth + this.gutter) * 2,
33672             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33673         });
33674         
33675         return pos;
33676         
33677     },
33678     
33679     getVerticalThreeBoxColPositions : function(x, y, box)
33680     {
33681         var pos = [];
33682         
33683         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33684             
33685             pos.push({
33686                 x : x,
33687                 y : y
33688             });
33689
33690             pos.push({
33691                 x : x + (this.unitWidth + this.gutter) * 1,
33692                 y : y
33693             });
33694             
33695             pos.push({
33696                 x : x + (this.unitWidth + this.gutter) * 2,
33697                 y : y
33698             });
33699             
33700             return pos;
33701             
33702         }
33703         
33704         if(box[0].size == 'xs' && box[1].size == 'xs'){
33705             
33706             pos.push({
33707                 x : x,
33708                 y : y
33709             });
33710
33711             pos.push({
33712                 x : x,
33713                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33714             });
33715             
33716             pos.push({
33717                 x : x + (this.unitWidth + this.gutter) * 1,
33718                 y : y
33719             });
33720             
33721             return pos;
33722             
33723         }
33724         
33725         pos.push({
33726             x : x,
33727             y : y
33728         });
33729
33730         pos.push({
33731             x : x + (this.unitWidth + this.gutter) * 2,
33732             y : y
33733         });
33734
33735         pos.push({
33736             x : x + (this.unitWidth + this.gutter) * 2,
33737             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33738         });
33739             
33740         return pos;
33741         
33742     },
33743     
33744     getVerticalFourBoxColPositions : function(x, y, box)
33745     {
33746         var pos = [];
33747         
33748         if(box[0].size == 'xs'){
33749             
33750             pos.push({
33751                 x : x,
33752                 y : y
33753             });
33754
33755             pos.push({
33756                 x : x,
33757                 y : y + (this.unitHeight + this.gutter) * 1
33758             });
33759             
33760             pos.push({
33761                 x : x,
33762                 y : y + (this.unitHeight + this.gutter) * 2
33763             });
33764             
33765             pos.push({
33766                 x : x + (this.unitWidth + this.gutter) * 1,
33767                 y : y
33768             });
33769             
33770             return pos;
33771             
33772         }
33773         
33774         pos.push({
33775             x : x,
33776             y : y
33777         });
33778
33779         pos.push({
33780             x : x + (this.unitWidth + this.gutter) * 2,
33781             y : y
33782         });
33783
33784         pos.push({
33785             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
33786             y : y + (this.unitHeight + this.gutter) * 1
33787         });
33788
33789         pos.push({
33790             x : x + (this.unitWidth + this.gutter) * 2,
33791             y : y + (this.unitWidth + this.gutter) * 2
33792         });
33793
33794         return pos;
33795         
33796     },
33797     
33798     getHorizontalOneBoxColPositions : function(maxX, minY, box)
33799     {
33800         var pos = [];
33801         
33802         if(box[0].size == 'md-left'){
33803             pos.push({
33804                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33805                 y : minY
33806             });
33807             
33808             return pos;
33809         }
33810         
33811         if(box[0].size == 'md-right'){
33812             pos.push({
33813                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33814                 y : minY + (this.unitWidth + this.gutter) * 1
33815             });
33816             
33817             return pos;
33818         }
33819         
33820         var rand = Math.floor(Math.random() * (4 - box[0].y));
33821         
33822         pos.push({
33823             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33824             y : minY + (this.unitWidth + this.gutter) * rand
33825         });
33826         
33827         return pos;
33828         
33829     },
33830     
33831     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
33832     {
33833         var pos = [];
33834         
33835         if(box[0].size == 'xs'){
33836             
33837             pos.push({
33838                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33839                 y : minY
33840             });
33841
33842             pos.push({
33843                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33844                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
33845             });
33846             
33847             return pos;
33848             
33849         }
33850         
33851         pos.push({
33852             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33853             y : minY
33854         });
33855
33856         pos.push({
33857             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33858             y : minY + (this.unitWidth + this.gutter) * 2
33859         });
33860         
33861         return pos;
33862         
33863     },
33864     
33865     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
33866     {
33867         var pos = [];
33868         
33869         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33870             
33871             pos.push({
33872                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33873                 y : minY
33874             });
33875
33876             pos.push({
33877                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33878                 y : minY + (this.unitWidth + this.gutter) * 1
33879             });
33880             
33881             pos.push({
33882                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33883                 y : minY + (this.unitWidth + this.gutter) * 2
33884             });
33885             
33886             return pos;
33887             
33888         }
33889         
33890         if(box[0].size == 'xs' && box[1].size == 'xs'){
33891             
33892             pos.push({
33893                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33894                 y : minY
33895             });
33896
33897             pos.push({
33898                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33899                 y : minY
33900             });
33901             
33902             pos.push({
33903                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33904                 y : minY + (this.unitWidth + this.gutter) * 1
33905             });
33906             
33907             return pos;
33908             
33909         }
33910         
33911         pos.push({
33912             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33913             y : minY
33914         });
33915
33916         pos.push({
33917             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33918             y : minY + (this.unitWidth + this.gutter) * 2
33919         });
33920
33921         pos.push({
33922             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33923             y : minY + (this.unitWidth + this.gutter) * 2
33924         });
33925             
33926         return pos;
33927         
33928     },
33929     
33930     getHorizontalFourBoxColPositions : function(maxX, minY, box)
33931     {
33932         var pos = [];
33933         
33934         if(box[0].size == 'xs'){
33935             
33936             pos.push({
33937                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].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),
33943                 y : minY
33944             });
33945             
33946             pos.push({
33947                 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),
33948                 y : minY
33949             });
33950             
33951             pos.push({
33952                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
33953                 y : minY + (this.unitWidth + this.gutter) * 1
33954             });
33955             
33956             return pos;
33957             
33958         }
33959         
33960         pos.push({
33961             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33962             y : minY
33963         });
33964         
33965         pos.push({
33966             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].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),
33972             y : minY + (this.unitWidth + this.gutter) * 2
33973         });
33974         
33975         pos.push({
33976             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),
33977             y : minY + (this.unitWidth + this.gutter) * 2
33978         });
33979
33980         return pos;
33981         
33982     },
33983     
33984     /**
33985     * remove a Masonry Brick
33986     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
33987     */
33988     removeBrick : function(brick_id)
33989     {
33990         if (!brick_id) {
33991             return;
33992         }
33993         
33994         for (var i = 0; i<this.bricks.length; i++) {
33995             if (this.bricks[i].id == brick_id) {
33996                 this.bricks.splice(i,1);
33997                 this.el.dom.removeChild(Roo.get(brick_id).dom);
33998                 this.initial();
33999             }
34000         }
34001     },
34002     
34003     /**
34004     * adds a Masonry Brick
34005     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34006     */
34007     addBrick : function(cfg)
34008     {
34009         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34010         //this.register(cn);
34011         cn.parentId = this.id;
34012         cn.render(this.el);
34013         return cn;
34014     },
34015     
34016     /**
34017     * register a Masonry Brick
34018     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34019     */
34020     
34021     register : function(brick)
34022     {
34023         this.bricks.push(brick);
34024         brick.masonryId = this.id;
34025     },
34026     
34027     /**
34028     * clear all the Masonry Brick
34029     */
34030     clearAll : function()
34031     {
34032         this.bricks = [];
34033         //this.getChildContainer().dom.innerHTML = "";
34034         this.el.dom.innerHTML = '';
34035     },
34036     
34037     getSelected : function()
34038     {
34039         if (!this.selectedBrick) {
34040             return false;
34041         }
34042         
34043         return this.selectedBrick;
34044     }
34045 });
34046
34047 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34048     
34049     groups: {},
34050      /**
34051     * register a Masonry Layout
34052     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34053     */
34054     
34055     register : function(layout)
34056     {
34057         this.groups[layout.id] = layout;
34058     },
34059     /**
34060     * fetch a  Masonry Layout based on the masonry layout ID
34061     * @param {string} the masonry layout to add
34062     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34063     */
34064     
34065     get: function(layout_id) {
34066         if (typeof(this.groups[layout_id]) == 'undefined') {
34067             return false;
34068         }
34069         return this.groups[layout_id] ;
34070     }
34071     
34072     
34073     
34074 });
34075
34076  
34077
34078  /**
34079  *
34080  * This is based on 
34081  * http://masonry.desandro.com
34082  *
34083  * The idea is to render all the bricks based on vertical width...
34084  *
34085  * The original code extends 'outlayer' - we might need to use that....
34086  * 
34087  */
34088
34089
34090 /**
34091  * @class Roo.bootstrap.LayoutMasonryAuto
34092  * @extends Roo.bootstrap.Component
34093  * Bootstrap Layout Masonry class
34094  * 
34095  * @constructor
34096  * Create a new Element
34097  * @param {Object} config The config object
34098  */
34099
34100 Roo.bootstrap.LayoutMasonryAuto = function(config){
34101     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34102 };
34103
34104 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34105     
34106       /**
34107      * @cfg {Boolean} isFitWidth  - resize the width..
34108      */   
34109     isFitWidth : false,  // options..
34110     /**
34111      * @cfg {Boolean} isOriginLeft = left align?
34112      */   
34113     isOriginLeft : true,
34114     /**
34115      * @cfg {Boolean} isOriginTop = top align?
34116      */   
34117     isOriginTop : false,
34118     /**
34119      * @cfg {Boolean} isLayoutInstant = no animation?
34120      */   
34121     isLayoutInstant : false, // needed?
34122     /**
34123      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34124      */   
34125     isResizingContainer : true,
34126     /**
34127      * @cfg {Number} columnWidth  width of the columns 
34128      */   
34129     
34130     columnWidth : 0,
34131     
34132     /**
34133      * @cfg {Number} maxCols maximum number of columns
34134      */   
34135     
34136     maxCols: 0,
34137     /**
34138      * @cfg {Number} padHeight padding below box..
34139      */   
34140     
34141     padHeight : 10, 
34142     
34143     /**
34144      * @cfg {Boolean} isAutoInitial defalut true
34145      */   
34146     
34147     isAutoInitial : true, 
34148     
34149     // private?
34150     gutter : 0,
34151     
34152     containerWidth: 0,
34153     initialColumnWidth : 0,
34154     currentSize : null,
34155     
34156     colYs : null, // array.
34157     maxY : 0,
34158     padWidth: 10,
34159     
34160     
34161     tag: 'div',
34162     cls: '',
34163     bricks: null, //CompositeElement
34164     cols : 0, // array?
34165     // element : null, // wrapped now this.el
34166     _isLayoutInited : null, 
34167     
34168     
34169     getAutoCreate : function(){
34170         
34171         var cfg = {
34172             tag: this.tag,
34173             cls: 'blog-masonary-wrapper ' + this.cls,
34174             cn : {
34175                 cls : 'mas-boxes masonary'
34176             }
34177         };
34178         
34179         return cfg;
34180     },
34181     
34182     getChildContainer: function( )
34183     {
34184         if (this.boxesEl) {
34185             return this.boxesEl;
34186         }
34187         
34188         this.boxesEl = this.el.select('.mas-boxes').first();
34189         
34190         return this.boxesEl;
34191     },
34192     
34193     
34194     initEvents : function()
34195     {
34196         var _this = this;
34197         
34198         if(this.isAutoInitial){
34199             Roo.log('hook children rendered');
34200             this.on('childrenrendered', function() {
34201                 Roo.log('children rendered');
34202                 _this.initial();
34203             } ,this);
34204         }
34205         
34206     },
34207     
34208     initial : function()
34209     {
34210         this.reloadItems();
34211
34212         this.currentSize = this.el.getBox(true);
34213
34214         /// was window resize... - let's see if this works..
34215         Roo.EventManager.onWindowResize(this.resize, this); 
34216
34217         if(!this.isAutoInitial){
34218             this.layout();
34219             return;
34220         }
34221         
34222         this.layout.defer(500,this);
34223     },
34224     
34225     reloadItems: function()
34226     {
34227         this.bricks = this.el.select('.masonry-brick', true);
34228         
34229         this.bricks.each(function(b) {
34230             //Roo.log(b.getSize());
34231             if (!b.attr('originalwidth')) {
34232                 b.attr('originalwidth',  b.getSize().width);
34233             }
34234             
34235         });
34236         
34237         Roo.log(this.bricks.elements.length);
34238     },
34239     
34240     resize : function()
34241     {
34242         Roo.log('resize');
34243         var cs = this.el.getBox(true);
34244         
34245         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34246             Roo.log("no change in with or X");
34247             return;
34248         }
34249         this.currentSize = cs;
34250         this.layout();
34251     },
34252     
34253     layout : function()
34254     {
34255          Roo.log('layout');
34256         this._resetLayout();
34257         //this._manageStamps();
34258       
34259         // don't animate first layout
34260         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34261         this.layoutItems( isInstant );
34262       
34263         // flag for initalized
34264         this._isLayoutInited = true;
34265     },
34266     
34267     layoutItems : function( isInstant )
34268     {
34269         //var items = this._getItemsForLayout( this.items );
34270         // original code supports filtering layout items.. we just ignore it..
34271         
34272         this._layoutItems( this.bricks , isInstant );
34273       
34274         this._postLayout();
34275     },
34276     _layoutItems : function ( items , isInstant)
34277     {
34278        //this.fireEvent( 'layout', this, items );
34279     
34280
34281         if ( !items || !items.elements.length ) {
34282           // no items, emit event with empty array
34283             return;
34284         }
34285
34286         var queue = [];
34287         items.each(function(item) {
34288             Roo.log("layout item");
34289             Roo.log(item);
34290             // get x/y object from method
34291             var position = this._getItemLayoutPosition( item );
34292             // enqueue
34293             position.item = item;
34294             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34295             queue.push( position );
34296         }, this);
34297       
34298         this._processLayoutQueue( queue );
34299     },
34300     /** Sets position of item in DOM
34301     * @param {Element} item
34302     * @param {Number} x - horizontal position
34303     * @param {Number} y - vertical position
34304     * @param {Boolean} isInstant - disables transitions
34305     */
34306     _processLayoutQueue : function( queue )
34307     {
34308         for ( var i=0, len = queue.length; i < len; i++ ) {
34309             var obj = queue[i];
34310             obj.item.position('absolute');
34311             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34312         }
34313     },
34314       
34315     
34316     /**
34317     * Any logic you want to do after each layout,
34318     * i.e. size the container
34319     */
34320     _postLayout : function()
34321     {
34322         this.resizeContainer();
34323     },
34324     
34325     resizeContainer : function()
34326     {
34327         if ( !this.isResizingContainer ) {
34328             return;
34329         }
34330         var size = this._getContainerSize();
34331         if ( size ) {
34332             this.el.setSize(size.width,size.height);
34333             this.boxesEl.setSize(size.width,size.height);
34334         }
34335     },
34336     
34337     
34338     
34339     _resetLayout : function()
34340     {
34341         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34342         this.colWidth = this.el.getWidth();
34343         //this.gutter = this.el.getWidth(); 
34344         
34345         this.measureColumns();
34346
34347         // reset column Y
34348         var i = this.cols;
34349         this.colYs = [];
34350         while (i--) {
34351             this.colYs.push( 0 );
34352         }
34353     
34354         this.maxY = 0;
34355     },
34356
34357     measureColumns : function()
34358     {
34359         this.getContainerWidth();
34360       // if columnWidth is 0, default to outerWidth of first item
34361         if ( !this.columnWidth ) {
34362             var firstItem = this.bricks.first();
34363             Roo.log(firstItem);
34364             this.columnWidth  = this.containerWidth;
34365             if (firstItem && firstItem.attr('originalwidth') ) {
34366                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34367             }
34368             // columnWidth fall back to item of first element
34369             Roo.log("set column width?");
34370                         this.initialColumnWidth = this.columnWidth  ;
34371
34372             // if first elem has no width, default to size of container
34373             
34374         }
34375         
34376         
34377         if (this.initialColumnWidth) {
34378             this.columnWidth = this.initialColumnWidth;
34379         }
34380         
34381         
34382             
34383         // column width is fixed at the top - however if container width get's smaller we should
34384         // reduce it...
34385         
34386         // this bit calcs how man columns..
34387             
34388         var columnWidth = this.columnWidth += this.gutter;
34389       
34390         // calculate columns
34391         var containerWidth = this.containerWidth + this.gutter;
34392         
34393         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34394         // fix rounding errors, typically with gutters
34395         var excess = columnWidth - containerWidth % columnWidth;
34396         
34397         
34398         // if overshoot is less than a pixel, round up, otherwise floor it
34399         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34400         cols = Math[ mathMethod ]( cols );
34401         this.cols = Math.max( cols, 1 );
34402         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34403         
34404          // padding positioning..
34405         var totalColWidth = this.cols * this.columnWidth;
34406         var padavail = this.containerWidth - totalColWidth;
34407         // so for 2 columns - we need 3 'pads'
34408         
34409         var padNeeded = (1+this.cols) * this.padWidth;
34410         
34411         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34412         
34413         this.columnWidth += padExtra
34414         //this.padWidth = Math.floor(padavail /  ( this.cols));
34415         
34416         // adjust colum width so that padding is fixed??
34417         
34418         // we have 3 columns ... total = width * 3
34419         // we have X left over... that should be used by 
34420         
34421         //if (this.expandC) {
34422             
34423         //}
34424         
34425         
34426         
34427     },
34428     
34429     getContainerWidth : function()
34430     {
34431        /* // container is parent if fit width
34432         var container = this.isFitWidth ? this.element.parentNode : this.element;
34433         // check that this.size and size are there
34434         // IE8 triggers resize on body size change, so they might not be
34435         
34436         var size = getSize( container );  //FIXME
34437         this.containerWidth = size && size.innerWidth; //FIXME
34438         */
34439          
34440         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34441         
34442     },
34443     
34444     _getItemLayoutPosition : function( item )  // what is item?
34445     {
34446         // we resize the item to our columnWidth..
34447       
34448         item.setWidth(this.columnWidth);
34449         item.autoBoxAdjust  = false;
34450         
34451         var sz = item.getSize();
34452  
34453         // how many columns does this brick span
34454         var remainder = this.containerWidth % this.columnWidth;
34455         
34456         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34457         // round if off by 1 pixel, otherwise use ceil
34458         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34459         colSpan = Math.min( colSpan, this.cols );
34460         
34461         // normally this should be '1' as we dont' currently allow multi width columns..
34462         
34463         var colGroup = this._getColGroup( colSpan );
34464         // get the minimum Y value from the columns
34465         var minimumY = Math.min.apply( Math, colGroup );
34466         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34467         
34468         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34469          
34470         // position the brick
34471         var position = {
34472             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34473             y: this.currentSize.y + minimumY + this.padHeight
34474         };
34475         
34476         Roo.log(position);
34477         // apply setHeight to necessary columns
34478         var setHeight = minimumY + sz.height + this.padHeight;
34479         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34480         
34481         var setSpan = this.cols + 1 - colGroup.length;
34482         for ( var i = 0; i < setSpan; i++ ) {
34483           this.colYs[ shortColIndex + i ] = setHeight ;
34484         }
34485       
34486         return position;
34487     },
34488     
34489     /**
34490      * @param {Number} colSpan - number of columns the element spans
34491      * @returns {Array} colGroup
34492      */
34493     _getColGroup : function( colSpan )
34494     {
34495         if ( colSpan < 2 ) {
34496           // if brick spans only one column, use all the column Ys
34497           return this.colYs;
34498         }
34499       
34500         var colGroup = [];
34501         // how many different places could this brick fit horizontally
34502         var groupCount = this.cols + 1 - colSpan;
34503         // for each group potential horizontal position
34504         for ( var i = 0; i < groupCount; i++ ) {
34505           // make an array of colY values for that one group
34506           var groupColYs = this.colYs.slice( i, i + colSpan );
34507           // and get the max value of the array
34508           colGroup[i] = Math.max.apply( Math, groupColYs );
34509         }
34510         return colGroup;
34511     },
34512     /*
34513     _manageStamp : function( stamp )
34514     {
34515         var stampSize =  stamp.getSize();
34516         var offset = stamp.getBox();
34517         // get the columns that this stamp affects
34518         var firstX = this.isOriginLeft ? offset.x : offset.right;
34519         var lastX = firstX + stampSize.width;
34520         var firstCol = Math.floor( firstX / this.columnWidth );
34521         firstCol = Math.max( 0, firstCol );
34522         
34523         var lastCol = Math.floor( lastX / this.columnWidth );
34524         // lastCol should not go over if multiple of columnWidth #425
34525         lastCol -= lastX % this.columnWidth ? 0 : 1;
34526         lastCol = Math.min( this.cols - 1, lastCol );
34527         
34528         // set colYs to bottom of the stamp
34529         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34530             stampSize.height;
34531             
34532         for ( var i = firstCol; i <= lastCol; i++ ) {
34533           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34534         }
34535     },
34536     */
34537     
34538     _getContainerSize : function()
34539     {
34540         this.maxY = Math.max.apply( Math, this.colYs );
34541         var size = {
34542             height: this.maxY
34543         };
34544       
34545         if ( this.isFitWidth ) {
34546             size.width = this._getContainerFitWidth();
34547         }
34548       
34549         return size;
34550     },
34551     
34552     _getContainerFitWidth : function()
34553     {
34554         var unusedCols = 0;
34555         // count unused columns
34556         var i = this.cols;
34557         while ( --i ) {
34558           if ( this.colYs[i] !== 0 ) {
34559             break;
34560           }
34561           unusedCols++;
34562         }
34563         // fit container to columns that have been used
34564         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34565     },
34566     
34567     needsResizeLayout : function()
34568     {
34569         var previousWidth = this.containerWidth;
34570         this.getContainerWidth();
34571         return previousWidth !== this.containerWidth;
34572     }
34573  
34574 });
34575
34576  
34577
34578  /*
34579  * - LGPL
34580  *
34581  * element
34582  * 
34583  */
34584
34585 /**
34586  * @class Roo.bootstrap.MasonryBrick
34587  * @extends Roo.bootstrap.Component
34588  * Bootstrap MasonryBrick class
34589  * 
34590  * @constructor
34591  * Create a new MasonryBrick
34592  * @param {Object} config The config object
34593  */
34594
34595 Roo.bootstrap.MasonryBrick = function(config){
34596     
34597     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34598     
34599     Roo.bootstrap.MasonryBrick.register(this);
34600     
34601     this.addEvents({
34602         // raw events
34603         /**
34604          * @event click
34605          * When a MasonryBrick is clcik
34606          * @param {Roo.bootstrap.MasonryBrick} this
34607          * @param {Roo.EventObject} e
34608          */
34609         "click" : true
34610     });
34611 };
34612
34613 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
34614     
34615     /**
34616      * @cfg {String} title
34617      */   
34618     title : '',
34619     /**
34620      * @cfg {String} html
34621      */   
34622     html : '',
34623     /**
34624      * @cfg {String} bgimage
34625      */   
34626     bgimage : '',
34627     /**
34628      * @cfg {String} videourl
34629      */   
34630     videourl : '',
34631     /**
34632      * @cfg {String} cls
34633      */   
34634     cls : '',
34635     /**
34636      * @cfg {String} href
34637      */   
34638     href : '',
34639     /**
34640      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34641      */   
34642     size : 'xs',
34643     
34644     /**
34645      * @cfg {String} placetitle (center|bottom)
34646      */   
34647     placetitle : '',
34648     
34649     /**
34650      * @cfg {Boolean} isFitContainer defalut true
34651      */   
34652     isFitContainer : true, 
34653     
34654     /**
34655      * @cfg {Boolean} preventDefault defalut false
34656      */   
34657     preventDefault : false, 
34658     
34659     /**
34660      * @cfg {Boolean} inverse defalut false
34661      */   
34662     maskInverse : false, 
34663     
34664     getAutoCreate : function()
34665     {
34666         if(!this.isFitContainer){
34667             return this.getSplitAutoCreate();
34668         }
34669         
34670         var cls = 'masonry-brick masonry-brick-full';
34671         
34672         if(this.href.length){
34673             cls += ' masonry-brick-link';
34674         }
34675         
34676         if(this.bgimage.length){
34677             cls += ' masonry-brick-image';
34678         }
34679         
34680         if(this.maskInverse){
34681             cls += ' mask-inverse';
34682         }
34683         
34684         if(!this.html.length && !this.maskInverse && !this.videourl.length){
34685             cls += ' enable-mask';
34686         }
34687         
34688         if(this.size){
34689             cls += ' masonry-' + this.size + '-brick';
34690         }
34691         
34692         if(this.placetitle.length){
34693             
34694             switch (this.placetitle) {
34695                 case 'center' :
34696                     cls += ' masonry-center-title';
34697                     break;
34698                 case 'bottom' :
34699                     cls += ' masonry-bottom-title';
34700                     break;
34701                 default:
34702                     break;
34703             }
34704             
34705         } else {
34706             if(!this.html.length && !this.bgimage.length){
34707                 cls += ' masonry-center-title';
34708             }
34709
34710             if(!this.html.length && this.bgimage.length){
34711                 cls += ' masonry-bottom-title';
34712             }
34713         }
34714         
34715         if(this.cls){
34716             cls += ' ' + this.cls;
34717         }
34718         
34719         var cfg = {
34720             tag: (this.href.length) ? 'a' : 'div',
34721             cls: cls,
34722             cn: [
34723                 {
34724                     tag: 'div',
34725                     cls: 'masonry-brick-mask'
34726                 },
34727                 {
34728                     tag: 'div',
34729                     cls: 'masonry-brick-paragraph',
34730                     cn: []
34731                 }
34732             ]
34733         };
34734         
34735         if(this.href.length){
34736             cfg.href = this.href;
34737         }
34738         
34739         var cn = cfg.cn[1].cn;
34740         
34741         if(this.title.length){
34742             cn.push({
34743                 tag: 'h4',
34744                 cls: 'masonry-brick-title',
34745                 html: this.title
34746             });
34747         }
34748         
34749         if(this.html.length){
34750             cn.push({
34751                 tag: 'p',
34752                 cls: 'masonry-brick-text',
34753                 html: this.html
34754             });
34755         }
34756         
34757         if (!this.title.length && !this.html.length) {
34758             cfg.cn[1].cls += ' hide';
34759         }
34760         
34761         if(this.bgimage.length){
34762             cfg.cn.push({
34763                 tag: 'img',
34764                 cls: 'masonry-brick-image-view',
34765                 src: this.bgimage
34766             });
34767         }
34768         
34769         if(this.videourl.length){
34770             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34771             // youtube support only?
34772             cfg.cn.push({
34773                 tag: 'iframe',
34774                 cls: 'masonry-brick-image-view',
34775                 src: vurl,
34776                 frameborder : 0,
34777                 allowfullscreen : true
34778             });
34779         }
34780         
34781         return cfg;
34782         
34783     },
34784     
34785     getSplitAutoCreate : function()
34786     {
34787         var cls = 'masonry-brick masonry-brick-split';
34788         
34789         if(this.href.length){
34790             cls += ' masonry-brick-link';
34791         }
34792         
34793         if(this.bgimage.length){
34794             cls += ' masonry-brick-image';
34795         }
34796         
34797         if(this.size){
34798             cls += ' masonry-' + this.size + '-brick';
34799         }
34800         
34801         switch (this.placetitle) {
34802             case 'center' :
34803                 cls += ' masonry-center-title';
34804                 break;
34805             case 'bottom' :
34806                 cls += ' masonry-bottom-title';
34807                 break;
34808             default:
34809                 if(!this.bgimage.length){
34810                     cls += ' masonry-center-title';
34811                 }
34812
34813                 if(this.bgimage.length){
34814                     cls += ' masonry-bottom-title';
34815                 }
34816                 break;
34817         }
34818         
34819         if(this.cls){
34820             cls += ' ' + this.cls;
34821         }
34822         
34823         var cfg = {
34824             tag: (this.href.length) ? 'a' : 'div',
34825             cls: cls,
34826             cn: [
34827                 {
34828                     tag: 'div',
34829                     cls: 'masonry-brick-split-head',
34830                     cn: [
34831                         {
34832                             tag: 'div',
34833                             cls: 'masonry-brick-paragraph',
34834                             cn: []
34835                         }
34836                     ]
34837                 },
34838                 {
34839                     tag: 'div',
34840                     cls: 'masonry-brick-split-body',
34841                     cn: []
34842                 }
34843             ]
34844         };
34845         
34846         if(this.href.length){
34847             cfg.href = this.href;
34848         }
34849         
34850         if(this.title.length){
34851             cfg.cn[0].cn[0].cn.push({
34852                 tag: 'h4',
34853                 cls: 'masonry-brick-title',
34854                 html: this.title
34855             });
34856         }
34857         
34858         if(this.html.length){
34859             cfg.cn[1].cn.push({
34860                 tag: 'p',
34861                 cls: 'masonry-brick-text',
34862                 html: this.html
34863             });
34864         }
34865
34866         if(this.bgimage.length){
34867             cfg.cn[0].cn.push({
34868                 tag: 'img',
34869                 cls: 'masonry-brick-image-view',
34870                 src: this.bgimage
34871             });
34872         }
34873         
34874         if(this.videourl.length){
34875             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34876             // youtube support only?
34877             cfg.cn[0].cn.cn.push({
34878                 tag: 'iframe',
34879                 cls: 'masonry-brick-image-view',
34880                 src: vurl,
34881                 frameborder : 0,
34882                 allowfullscreen : true
34883             });
34884         }
34885         
34886         return cfg;
34887     },
34888     
34889     initEvents: function() 
34890     {
34891         switch (this.size) {
34892             case 'xs' :
34893                 this.x = 1;
34894                 this.y = 1;
34895                 break;
34896             case 'sm' :
34897                 this.x = 2;
34898                 this.y = 2;
34899                 break;
34900             case 'md' :
34901             case 'md-left' :
34902             case 'md-right' :
34903                 this.x = 3;
34904                 this.y = 3;
34905                 break;
34906             case 'tall' :
34907                 this.x = 2;
34908                 this.y = 3;
34909                 break;
34910             case 'wide' :
34911                 this.x = 3;
34912                 this.y = 2;
34913                 break;
34914             case 'wide-thin' :
34915                 this.x = 3;
34916                 this.y = 1;
34917                 break;
34918                         
34919             default :
34920                 break;
34921         }
34922         
34923         if(Roo.isTouch){
34924             this.el.on('touchstart', this.onTouchStart, this);
34925             this.el.on('touchmove', this.onTouchMove, this);
34926             this.el.on('touchend', this.onTouchEnd, this);
34927             this.el.on('contextmenu', this.onContextMenu, this);
34928         } else {
34929             this.el.on('mouseenter'  ,this.enter, this);
34930             this.el.on('mouseleave', this.leave, this);
34931             this.el.on('click', this.onClick, this);
34932         }
34933         
34934         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
34935             this.parent().bricks.push(this);   
34936         }
34937         
34938     },
34939     
34940     onClick: function(e, el)
34941     {
34942         var time = this.endTimer - this.startTimer;
34943         // Roo.log(e.preventDefault());
34944         if(Roo.isTouch){
34945             if(time > 1000){
34946                 e.preventDefault();
34947                 return;
34948             }
34949         }
34950         
34951         if(!this.preventDefault){
34952             return;
34953         }
34954         
34955         e.preventDefault();
34956         
34957         if (this.activeClass != '') {
34958             this.selectBrick();
34959         }
34960         
34961         this.fireEvent('click', this, e);
34962     },
34963     
34964     enter: function(e, el)
34965     {
34966         e.preventDefault();
34967         
34968         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
34969             return;
34970         }
34971         
34972         if(this.bgimage.length && this.html.length){
34973             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
34974         }
34975     },
34976     
34977     leave: function(e, el)
34978     {
34979         e.preventDefault();
34980         
34981         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
34982             return;
34983         }
34984         
34985         if(this.bgimage.length && this.html.length){
34986             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
34987         }
34988     },
34989     
34990     onTouchStart: function(e, el)
34991     {
34992 //        e.preventDefault();
34993         
34994         this.touchmoved = false;
34995         
34996         if(!this.isFitContainer){
34997             return;
34998         }
34999         
35000         if(!this.bgimage.length || !this.html.length){
35001             return;
35002         }
35003         
35004         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35005         
35006         this.timer = new Date().getTime();
35007         
35008     },
35009     
35010     onTouchMove: function(e, el)
35011     {
35012         this.touchmoved = true;
35013     },
35014     
35015     onContextMenu : function(e,el)
35016     {
35017         e.preventDefault();
35018         e.stopPropagation();
35019         return false;
35020     },
35021     
35022     onTouchEnd: function(e, el)
35023     {
35024 //        e.preventDefault();
35025         
35026         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35027         
35028             this.leave(e,el);
35029             
35030             return;
35031         }
35032         
35033         if(!this.bgimage.length || !this.html.length){
35034             
35035             if(this.href.length){
35036                 window.location.href = this.href;
35037             }
35038             
35039             return;
35040         }
35041         
35042         if(!this.isFitContainer){
35043             return;
35044         }
35045         
35046         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35047         
35048         window.location.href = this.href;
35049     },
35050     
35051     //selection on single brick only
35052     selectBrick : function() {
35053         
35054         if (!this.parentId) {
35055             return;
35056         }
35057         
35058         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35059         var index = m.selectedBrick.indexOf(this.id);
35060         
35061         if ( index > -1) {
35062             m.selectedBrick.splice(index,1);
35063             this.el.removeClass(this.activeClass);
35064             return;
35065         }
35066         
35067         for(var i = 0; i < m.selectedBrick.length; i++) {
35068             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35069             b.el.removeClass(b.activeClass);
35070         }
35071         
35072         m.selectedBrick = [];
35073         
35074         m.selectedBrick.push(this.id);
35075         this.el.addClass(this.activeClass);
35076         return;
35077     },
35078     
35079     isSelected : function(){
35080         return this.el.hasClass(this.activeClass);
35081         
35082     }
35083 });
35084
35085 Roo.apply(Roo.bootstrap.MasonryBrick, {
35086     
35087     //groups: {},
35088     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35089      /**
35090     * register a Masonry Brick
35091     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35092     */
35093     
35094     register : function(brick)
35095     {
35096         //this.groups[brick.id] = brick;
35097         this.groups.add(brick.id, brick);
35098     },
35099     /**
35100     * fetch a  masonry brick based on the masonry brick ID
35101     * @param {string} the masonry brick to add
35102     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35103     */
35104     
35105     get: function(brick_id) 
35106     {
35107         // if (typeof(this.groups[brick_id]) == 'undefined') {
35108         //     return false;
35109         // }
35110         // return this.groups[brick_id] ;
35111         
35112         if(this.groups.key(brick_id)) {
35113             return this.groups.key(brick_id);
35114         }
35115         
35116         return false;
35117     }
35118     
35119     
35120     
35121 });
35122
35123  /*
35124  * - LGPL
35125  *
35126  * element
35127  * 
35128  */
35129
35130 /**
35131  * @class Roo.bootstrap.Brick
35132  * @extends Roo.bootstrap.Component
35133  * Bootstrap Brick class
35134  * 
35135  * @constructor
35136  * Create a new Brick
35137  * @param {Object} config The config object
35138  */
35139
35140 Roo.bootstrap.Brick = function(config){
35141     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35142     
35143     this.addEvents({
35144         // raw events
35145         /**
35146          * @event click
35147          * When a Brick is click
35148          * @param {Roo.bootstrap.Brick} this
35149          * @param {Roo.EventObject} e
35150          */
35151         "click" : true
35152     });
35153 };
35154
35155 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35156     
35157     /**
35158      * @cfg {String} title
35159      */   
35160     title : '',
35161     /**
35162      * @cfg {String} html
35163      */   
35164     html : '',
35165     /**
35166      * @cfg {String} bgimage
35167      */   
35168     bgimage : '',
35169     /**
35170      * @cfg {String} cls
35171      */   
35172     cls : '',
35173     /**
35174      * @cfg {String} href
35175      */   
35176     href : '',
35177     /**
35178      * @cfg {String} video
35179      */   
35180     video : '',
35181     /**
35182      * @cfg {Boolean} square
35183      */   
35184     square : true,
35185     
35186     getAutoCreate : function()
35187     {
35188         var cls = 'roo-brick';
35189         
35190         if(this.href.length){
35191             cls += ' roo-brick-link';
35192         }
35193         
35194         if(this.bgimage.length){
35195             cls += ' roo-brick-image';
35196         }
35197         
35198         if(!this.html.length && !this.bgimage.length){
35199             cls += ' roo-brick-center-title';
35200         }
35201         
35202         if(!this.html.length && this.bgimage.length){
35203             cls += ' roo-brick-bottom-title';
35204         }
35205         
35206         if(this.cls){
35207             cls += ' ' + this.cls;
35208         }
35209         
35210         var cfg = {
35211             tag: (this.href.length) ? 'a' : 'div',
35212             cls: cls,
35213             cn: [
35214                 {
35215                     tag: 'div',
35216                     cls: 'roo-brick-paragraph',
35217                     cn: []
35218                 }
35219             ]
35220         };
35221         
35222         if(this.href.length){
35223             cfg.href = this.href;
35224         }
35225         
35226         var cn = cfg.cn[0].cn;
35227         
35228         if(this.title.length){
35229             cn.push({
35230                 tag: 'h4',
35231                 cls: 'roo-brick-title',
35232                 html: this.title
35233             });
35234         }
35235         
35236         if(this.html.length){
35237             cn.push({
35238                 tag: 'p',
35239                 cls: 'roo-brick-text',
35240                 html: this.html
35241             });
35242         } else {
35243             cn.cls += ' hide';
35244         }
35245         
35246         if(this.bgimage.length){
35247             cfg.cn.push({
35248                 tag: 'img',
35249                 cls: 'roo-brick-image-view',
35250                 src: this.bgimage
35251             });
35252         }
35253         
35254         return cfg;
35255     },
35256     
35257     initEvents: function() 
35258     {
35259         if(this.title.length || this.html.length){
35260             this.el.on('mouseenter'  ,this.enter, this);
35261             this.el.on('mouseleave', this.leave, this);
35262         }
35263         
35264         Roo.EventManager.onWindowResize(this.resize, this); 
35265         
35266         if(this.bgimage.length){
35267             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35268             this.imageEl.on('load', this.onImageLoad, this);
35269             return;
35270         }
35271         
35272         this.resize();
35273     },
35274     
35275     onImageLoad : function()
35276     {
35277         this.resize();
35278     },
35279     
35280     resize : function()
35281     {
35282         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35283         
35284         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35285         
35286         if(this.bgimage.length){
35287             var image = this.el.select('.roo-brick-image-view', true).first();
35288             
35289             image.setWidth(paragraph.getWidth());
35290             
35291             if(this.square){
35292                 image.setHeight(paragraph.getWidth());
35293             }
35294             
35295             this.el.setHeight(image.getHeight());
35296             paragraph.setHeight(image.getHeight());
35297             
35298         }
35299         
35300     },
35301     
35302     enter: function(e, el)
35303     {
35304         e.preventDefault();
35305         
35306         if(this.bgimage.length){
35307             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35308             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35309         }
35310     },
35311     
35312     leave: function(e, el)
35313     {
35314         e.preventDefault();
35315         
35316         if(this.bgimage.length){
35317             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35318             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35319         }
35320     }
35321     
35322 });
35323
35324  
35325
35326  /*
35327  * - LGPL
35328  *
35329  * Number field 
35330  */
35331
35332 /**
35333  * @class Roo.bootstrap.NumberField
35334  * @extends Roo.bootstrap.Input
35335  * Bootstrap NumberField class
35336  * 
35337  * 
35338  * 
35339  * 
35340  * @constructor
35341  * Create a new NumberField
35342  * @param {Object} config The config object
35343  */
35344
35345 Roo.bootstrap.NumberField = function(config){
35346     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35347 };
35348
35349 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35350     
35351     /**
35352      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35353      */
35354     allowDecimals : true,
35355     /**
35356      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35357      */
35358     decimalSeparator : ".",
35359     /**
35360      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35361      */
35362     decimalPrecision : 2,
35363     /**
35364      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35365      */
35366     allowNegative : true,
35367     
35368     /**
35369      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35370      */
35371     allowZero: true,
35372     /**
35373      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35374      */
35375     minValue : Number.NEGATIVE_INFINITY,
35376     /**
35377      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35378      */
35379     maxValue : Number.MAX_VALUE,
35380     /**
35381      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35382      */
35383     minText : "The minimum value for this field is {0}",
35384     /**
35385      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35386      */
35387     maxText : "The maximum value for this field is {0}",
35388     /**
35389      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35390      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35391      */
35392     nanText : "{0} is not a valid number",
35393     /**
35394      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35395      */
35396     thousandsDelimiter : false,
35397     /**
35398      * @cfg {String} valueAlign alignment of value
35399      */
35400     valueAlign : "left",
35401
35402     getAutoCreate : function()
35403     {
35404         var hiddenInput = {
35405             tag: 'input',
35406             type: 'hidden',
35407             id: Roo.id(),
35408             cls: 'hidden-number-input'
35409         };
35410         
35411         if (this.name) {
35412             hiddenInput.name = this.name;
35413         }
35414         
35415         this.name = '';
35416         
35417         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35418         
35419         this.name = hiddenInput.name;
35420         
35421         if(cfg.cn.length > 0) {
35422             cfg.cn.push(hiddenInput);
35423         }
35424         
35425         return cfg;
35426     },
35427
35428     // private
35429     initEvents : function()
35430     {   
35431         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35432         
35433         var allowed = "0123456789";
35434         
35435         if(this.allowDecimals){
35436             allowed += this.decimalSeparator;
35437         }
35438         
35439         if(this.allowNegative){
35440             allowed += "-";
35441         }
35442         
35443         if(this.thousandsDelimiter) {
35444             allowed += ",";
35445         }
35446         
35447         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35448         
35449         var keyPress = function(e){
35450             
35451             var k = e.getKey();
35452             
35453             var c = e.getCharCode();
35454             
35455             if(
35456                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35457                     allowed.indexOf(String.fromCharCode(c)) === -1
35458             ){
35459                 e.stopEvent();
35460                 return;
35461             }
35462             
35463             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35464                 return;
35465             }
35466             
35467             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35468                 e.stopEvent();
35469             }
35470         };
35471         
35472         this.el.on("keypress", keyPress, this);
35473     },
35474     
35475     validateValue : function(value)
35476     {
35477         
35478         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35479             return false;
35480         }
35481         
35482         var num = this.parseValue(value);
35483         
35484         if(isNaN(num)){
35485             this.markInvalid(String.format(this.nanText, value));
35486             return false;
35487         }
35488         
35489         if(num < this.minValue){
35490             this.markInvalid(String.format(this.minText, this.minValue));
35491             return false;
35492         }
35493         
35494         if(num > this.maxValue){
35495             this.markInvalid(String.format(this.maxText, this.maxValue));
35496             return false;
35497         }
35498         
35499         return true;
35500     },
35501
35502     getValue : function()
35503     {
35504         var v = this.hiddenEl().getValue();
35505         
35506         return this.fixPrecision(this.parseValue(v));
35507     },
35508
35509     parseValue : function(value)
35510     {
35511         if(this.thousandsDelimiter) {
35512             value += "";
35513             r = new RegExp(",", "g");
35514             value = value.replace(r, "");
35515         }
35516         
35517         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35518         return isNaN(value) ? '' : value;
35519     },
35520
35521     fixPrecision : function(value)
35522     {
35523         if(this.thousandsDelimiter) {
35524             value += "";
35525             r = new RegExp(",", "g");
35526             value = value.replace(r, "");
35527         }
35528         
35529         var nan = isNaN(value);
35530         
35531         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35532             return nan ? '' : value;
35533         }
35534         return parseFloat(value).toFixed(this.decimalPrecision);
35535     },
35536
35537     setValue : function(v)
35538     {
35539         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35540         
35541         this.value = v;
35542         
35543         if(this.rendered){
35544             
35545             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35546             
35547             this.inputEl().dom.value = (v == '') ? '' :
35548                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35549             
35550             if(!this.allowZero && v === '0') {
35551                 this.hiddenEl().dom.value = '';
35552                 this.inputEl().dom.value = '';
35553             }
35554             
35555             this.validate();
35556         }
35557     },
35558
35559     decimalPrecisionFcn : function(v)
35560     {
35561         return Math.floor(v);
35562     },
35563
35564     beforeBlur : function()
35565     {
35566         var v = this.parseValue(this.getRawValue());
35567         
35568         if(v || v === 0 || v === ''){
35569             this.setValue(v);
35570         }
35571     },
35572     
35573     hiddenEl : function()
35574     {
35575         return this.el.select('input.hidden-number-input',true).first();
35576     }
35577     
35578 });
35579
35580  
35581
35582 /*
35583 * Licence: LGPL
35584 */
35585
35586 /**
35587  * @class Roo.bootstrap.DocumentSlider
35588  * @extends Roo.bootstrap.Component
35589  * Bootstrap DocumentSlider class
35590  * 
35591  * @constructor
35592  * Create a new DocumentViewer
35593  * @param {Object} config The config object
35594  */
35595
35596 Roo.bootstrap.DocumentSlider = function(config){
35597     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35598     
35599     this.files = [];
35600     
35601     this.addEvents({
35602         /**
35603          * @event initial
35604          * Fire after initEvent
35605          * @param {Roo.bootstrap.DocumentSlider} this
35606          */
35607         "initial" : true,
35608         /**
35609          * @event update
35610          * Fire after update
35611          * @param {Roo.bootstrap.DocumentSlider} this
35612          */
35613         "update" : true,
35614         /**
35615          * @event click
35616          * Fire after click
35617          * @param {Roo.bootstrap.DocumentSlider} this
35618          */
35619         "click" : true
35620     });
35621 };
35622
35623 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
35624     
35625     files : false,
35626     
35627     indicator : 0,
35628     
35629     getAutoCreate : function()
35630     {
35631         var cfg = {
35632             tag : 'div',
35633             cls : 'roo-document-slider',
35634             cn : [
35635                 {
35636                     tag : 'div',
35637                     cls : 'roo-document-slider-header',
35638                     cn : [
35639                         {
35640                             tag : 'div',
35641                             cls : 'roo-document-slider-header-title'
35642                         }
35643                     ]
35644                 },
35645                 {
35646                     tag : 'div',
35647                     cls : 'roo-document-slider-body',
35648                     cn : [
35649                         {
35650                             tag : 'div',
35651                             cls : 'roo-document-slider-prev',
35652                             cn : [
35653                                 {
35654                                     tag : 'i',
35655                                     cls : 'fa fa-chevron-left'
35656                                 }
35657                             ]
35658                         },
35659                         {
35660                             tag : 'div',
35661                             cls : 'roo-document-slider-thumb',
35662                             cn : [
35663                                 {
35664                                     tag : 'img',
35665                                     cls : 'roo-document-slider-image'
35666                                 }
35667                             ]
35668                         },
35669                         {
35670                             tag : 'div',
35671                             cls : 'roo-document-slider-next',
35672                             cn : [
35673                                 {
35674                                     tag : 'i',
35675                                     cls : 'fa fa-chevron-right'
35676                                 }
35677                             ]
35678                         }
35679                     ]
35680                 }
35681             ]
35682         };
35683         
35684         return cfg;
35685     },
35686     
35687     initEvents : function()
35688     {
35689         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35690         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35691         
35692         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35693         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35694         
35695         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35696         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35697         
35698         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35699         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35700         
35701         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35702         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35703         
35704         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35705         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35706         
35707         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35708         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35709         
35710         this.thumbEl.on('click', this.onClick, this);
35711         
35712         this.prevIndicator.on('click', this.prev, this);
35713         
35714         this.nextIndicator.on('click', this.next, this);
35715         
35716     },
35717     
35718     initial : function()
35719     {
35720         if(this.files.length){
35721             this.indicator = 1;
35722             this.update()
35723         }
35724         
35725         this.fireEvent('initial', this);
35726     },
35727     
35728     update : function()
35729     {
35730         this.imageEl.attr('src', this.files[this.indicator - 1]);
35731         
35732         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35733         
35734         this.prevIndicator.show();
35735         
35736         if(this.indicator == 1){
35737             this.prevIndicator.hide();
35738         }
35739         
35740         this.nextIndicator.show();
35741         
35742         if(this.indicator == this.files.length){
35743             this.nextIndicator.hide();
35744         }
35745         
35746         this.thumbEl.scrollTo('top');
35747         
35748         this.fireEvent('update', this);
35749     },
35750     
35751     onClick : function(e)
35752     {
35753         e.preventDefault();
35754         
35755         this.fireEvent('click', this);
35756     },
35757     
35758     prev : function(e)
35759     {
35760         e.preventDefault();
35761         
35762         this.indicator = Math.max(1, this.indicator - 1);
35763         
35764         this.update();
35765     },
35766     
35767     next : function(e)
35768     {
35769         e.preventDefault();
35770         
35771         this.indicator = Math.min(this.files.length, this.indicator + 1);
35772         
35773         this.update();
35774     }
35775 });
35776 /*
35777  * - LGPL
35778  *
35779  * RadioSet
35780  *
35781  *
35782  */
35783
35784 /**
35785  * @class Roo.bootstrap.RadioSet
35786  * @extends Roo.bootstrap.Input
35787  * Bootstrap RadioSet class
35788  * @cfg {String} indicatorpos (left|right) default left
35789  * @cfg {Boolean} inline (true|false) inline the element (default true)
35790  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
35791  * @constructor
35792  * Create a new RadioSet
35793  * @param {Object} config The config object
35794  */
35795
35796 Roo.bootstrap.RadioSet = function(config){
35797     
35798     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
35799     
35800     this.radioes = [];
35801     
35802     Roo.bootstrap.RadioSet.register(this);
35803     
35804     this.addEvents({
35805         /**
35806         * @event check
35807         * Fires when the element is checked or unchecked.
35808         * @param {Roo.bootstrap.RadioSet} this This radio
35809         * @param {Roo.bootstrap.Radio} item The checked item
35810         */
35811        check : true,
35812        /**
35813         * @event click
35814         * Fires when the element is click.
35815         * @param {Roo.bootstrap.RadioSet} this This radio set
35816         * @param {Roo.bootstrap.Radio} item The checked item
35817         * @param {Roo.EventObject} e The event object
35818         */
35819        click : true
35820     });
35821     
35822 };
35823
35824 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
35825
35826     radioes : false,
35827     
35828     inline : true,
35829     
35830     weight : '',
35831     
35832     indicatorpos : 'left',
35833     
35834     getAutoCreate : function()
35835     {
35836         var label = {
35837             tag : 'label',
35838             cls : 'roo-radio-set-label',
35839             cn : [
35840                 {
35841                     tag : 'span',
35842                     html : this.fieldLabel
35843                 }
35844             ]
35845         };
35846         if (Roo.bootstrap.version == 3) {
35847             
35848             
35849             if(this.indicatorpos == 'left'){
35850                 label.cn.unshift({
35851                     tag : 'i',
35852                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
35853                     tooltip : 'This field is required'
35854                 });
35855             } else {
35856                 label.cn.push({
35857                     tag : 'i',
35858                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
35859                     tooltip : 'This field is required'
35860                 });
35861             }
35862         }
35863         var items = {
35864             tag : 'div',
35865             cls : 'roo-radio-set-items'
35866         };
35867         
35868         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
35869         
35870         if (align === 'left' && this.fieldLabel.length) {
35871             
35872             items = {
35873                 cls : "roo-radio-set-right", 
35874                 cn: [
35875                     items
35876                 ]
35877             };
35878             
35879             if(this.labelWidth > 12){
35880                 label.style = "width: " + this.labelWidth + 'px';
35881             }
35882             
35883             if(this.labelWidth < 13 && this.labelmd == 0){
35884                 this.labelmd = this.labelWidth;
35885             }
35886             
35887             if(this.labellg > 0){
35888                 label.cls += ' col-lg-' + this.labellg;
35889                 items.cls += ' col-lg-' + (12 - this.labellg);
35890             }
35891             
35892             if(this.labelmd > 0){
35893                 label.cls += ' col-md-' + this.labelmd;
35894                 items.cls += ' col-md-' + (12 - this.labelmd);
35895             }
35896             
35897             if(this.labelsm > 0){
35898                 label.cls += ' col-sm-' + this.labelsm;
35899                 items.cls += ' col-sm-' + (12 - this.labelsm);
35900             }
35901             
35902             if(this.labelxs > 0){
35903                 label.cls += ' col-xs-' + this.labelxs;
35904                 items.cls += ' col-xs-' + (12 - this.labelxs);
35905             }
35906         }
35907         
35908         var cfg = {
35909             tag : 'div',
35910             cls : 'roo-radio-set',
35911             cn : [
35912                 {
35913                     tag : 'input',
35914                     cls : 'roo-radio-set-input',
35915                     type : 'hidden',
35916                     name : this.name,
35917                     value : this.value ? this.value :  ''
35918                 },
35919                 label,
35920                 items
35921             ]
35922         };
35923         
35924         if(this.weight.length){
35925             cfg.cls += ' roo-radio-' + this.weight;
35926         }
35927         
35928         if(this.inline) {
35929             cfg.cls += ' roo-radio-set-inline';
35930         }
35931         
35932         var settings=this;
35933         ['xs','sm','md','lg'].map(function(size){
35934             if (settings[size]) {
35935                 cfg.cls += ' col-' + size + '-' + settings[size];
35936             }
35937         });
35938         
35939         return cfg;
35940         
35941     },
35942
35943     initEvents : function()
35944     {
35945         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
35946         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
35947         
35948         if(!this.fieldLabel.length){
35949             this.labelEl.hide();
35950         }
35951         
35952         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
35953         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
35954         
35955         this.indicator = this.indicatorEl();
35956         
35957         if(this.indicator){
35958             this.indicator.addClass('invisible');
35959         }
35960         
35961         this.originalValue = this.getValue();
35962         
35963     },
35964     
35965     inputEl: function ()
35966     {
35967         return this.el.select('.roo-radio-set-input', true).first();
35968     },
35969     
35970     getChildContainer : function()
35971     {
35972         return this.itemsEl;
35973     },
35974     
35975     register : function(item)
35976     {
35977         this.radioes.push(item);
35978         
35979     },
35980     
35981     validate : function()
35982     {   
35983         if(this.getVisibilityEl().hasClass('hidden')){
35984             return true;
35985         }
35986         
35987         var valid = false;
35988         
35989         Roo.each(this.radioes, function(i){
35990             if(!i.checked){
35991                 return;
35992             }
35993             
35994             valid = true;
35995             return false;
35996         });
35997         
35998         if(this.allowBlank) {
35999             return true;
36000         }
36001         
36002         if(this.disabled || valid){
36003             this.markValid();
36004             return true;
36005         }
36006         
36007         this.markInvalid();
36008         return false;
36009         
36010     },
36011     
36012     markValid : function()
36013     {
36014         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36015             this.indicatorEl().removeClass('visible');
36016             this.indicatorEl().addClass('invisible');
36017         }
36018         
36019         
36020         if (Roo.bootstrap.version == 3) {
36021             this.el.removeClass([this.invalidClass, this.validClass]);
36022             this.el.addClass(this.validClass);
36023         } else {
36024             this.el.removeClass(['is-invalid','is-valid']);
36025             this.el.addClass(['is-valid']);
36026         }
36027         this.fireEvent('valid', this);
36028     },
36029     
36030     markInvalid : function(msg)
36031     {
36032         if(this.allowBlank || this.disabled){
36033             return;
36034         }
36035         
36036         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36037             this.indicatorEl().removeClass('invisible');
36038             this.indicatorEl().addClass('visible');
36039         }
36040         if (Roo.bootstrap.version == 3) {
36041             this.el.removeClass([this.invalidClass, this.validClass]);
36042             this.el.addClass(this.invalidClass);
36043         } else {
36044             this.el.removeClass(['is-invalid','is-valid']);
36045             this.el.addClass(['is-invalid']);
36046         }
36047         
36048         this.fireEvent('invalid', this, msg);
36049         
36050     },
36051     
36052     setValue : function(v, suppressEvent)
36053     {   
36054         if(this.value === v){
36055             return;
36056         }
36057         
36058         this.value = v;
36059         
36060         if(this.rendered){
36061             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36062         }
36063         
36064         Roo.each(this.radioes, function(i){
36065             i.checked = false;
36066             i.el.removeClass('checked');
36067         });
36068         
36069         Roo.each(this.radioes, function(i){
36070             
36071             if(i.value === v || i.value.toString() === v.toString()){
36072                 i.checked = true;
36073                 i.el.addClass('checked');
36074                 
36075                 if(suppressEvent !== true){
36076                     this.fireEvent('check', this, i);
36077                 }
36078                 
36079                 return false;
36080             }
36081             
36082         }, this);
36083         
36084         this.validate();
36085     },
36086     
36087     clearInvalid : function(){
36088         
36089         if(!this.el || this.preventMark){
36090             return;
36091         }
36092         
36093         this.el.removeClass([this.invalidClass]);
36094         
36095         this.fireEvent('valid', this);
36096     }
36097     
36098 });
36099
36100 Roo.apply(Roo.bootstrap.RadioSet, {
36101     
36102     groups: {},
36103     
36104     register : function(set)
36105     {
36106         this.groups[set.name] = set;
36107     },
36108     
36109     get: function(name) 
36110     {
36111         if (typeof(this.groups[name]) == 'undefined') {
36112             return false;
36113         }
36114         
36115         return this.groups[name] ;
36116     }
36117     
36118 });
36119 /*
36120  * Based on:
36121  * Ext JS Library 1.1.1
36122  * Copyright(c) 2006-2007, Ext JS, LLC.
36123  *
36124  * Originally Released Under LGPL - original licence link has changed is not relivant.
36125  *
36126  * Fork - LGPL
36127  * <script type="text/javascript">
36128  */
36129
36130
36131 /**
36132  * @class Roo.bootstrap.SplitBar
36133  * @extends Roo.util.Observable
36134  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36135  * <br><br>
36136  * Usage:
36137  * <pre><code>
36138 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36139                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36140 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36141 split.minSize = 100;
36142 split.maxSize = 600;
36143 split.animate = true;
36144 split.on('moved', splitterMoved);
36145 </code></pre>
36146  * @constructor
36147  * Create a new SplitBar
36148  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36149  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36150  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36151  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36152                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36153                         position of the SplitBar).
36154  */
36155 Roo.bootstrap.SplitBar = function(cfg){
36156     
36157     /** @private */
36158     
36159     //{
36160     //  dragElement : elm
36161     //  resizingElement: el,
36162         // optional..
36163     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36164     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36165         // existingProxy ???
36166     //}
36167     
36168     this.el = Roo.get(cfg.dragElement, true);
36169     this.el.dom.unselectable = "on";
36170     /** @private */
36171     this.resizingEl = Roo.get(cfg.resizingElement, true);
36172
36173     /**
36174      * @private
36175      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36176      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36177      * @type Number
36178      */
36179     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36180     
36181     /**
36182      * The minimum size of the resizing element. (Defaults to 0)
36183      * @type Number
36184      */
36185     this.minSize = 0;
36186     
36187     /**
36188      * The maximum size of the resizing element. (Defaults to 2000)
36189      * @type Number
36190      */
36191     this.maxSize = 2000;
36192     
36193     /**
36194      * Whether to animate the transition to the new size
36195      * @type Boolean
36196      */
36197     this.animate = false;
36198     
36199     /**
36200      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36201      * @type Boolean
36202      */
36203     this.useShim = false;
36204     
36205     /** @private */
36206     this.shim = null;
36207     
36208     if(!cfg.existingProxy){
36209         /** @private */
36210         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36211     }else{
36212         this.proxy = Roo.get(cfg.existingProxy).dom;
36213     }
36214     /** @private */
36215     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36216     
36217     /** @private */
36218     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36219     
36220     /** @private */
36221     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36222     
36223     /** @private */
36224     this.dragSpecs = {};
36225     
36226     /**
36227      * @private The adapter to use to positon and resize elements
36228      */
36229     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36230     this.adapter.init(this);
36231     
36232     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36233         /** @private */
36234         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36235         this.el.addClass("roo-splitbar-h");
36236     }else{
36237         /** @private */
36238         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36239         this.el.addClass("roo-splitbar-v");
36240     }
36241     
36242     this.addEvents({
36243         /**
36244          * @event resize
36245          * Fires when the splitter is moved (alias for {@link #event-moved})
36246          * @param {Roo.bootstrap.SplitBar} this
36247          * @param {Number} newSize the new width or height
36248          */
36249         "resize" : true,
36250         /**
36251          * @event moved
36252          * Fires when the splitter is moved
36253          * @param {Roo.bootstrap.SplitBar} this
36254          * @param {Number} newSize the new width or height
36255          */
36256         "moved" : true,
36257         /**
36258          * @event beforeresize
36259          * Fires before the splitter is dragged
36260          * @param {Roo.bootstrap.SplitBar} this
36261          */
36262         "beforeresize" : true,
36263
36264         "beforeapply" : true
36265     });
36266
36267     Roo.util.Observable.call(this);
36268 };
36269
36270 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36271     onStartProxyDrag : function(x, y){
36272         this.fireEvent("beforeresize", this);
36273         if(!this.overlay){
36274             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36275             o.unselectable();
36276             o.enableDisplayMode("block");
36277             // all splitbars share the same overlay
36278             Roo.bootstrap.SplitBar.prototype.overlay = o;
36279         }
36280         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36281         this.overlay.show();
36282         Roo.get(this.proxy).setDisplayed("block");
36283         var size = this.adapter.getElementSize(this);
36284         this.activeMinSize = this.getMinimumSize();;
36285         this.activeMaxSize = this.getMaximumSize();;
36286         var c1 = size - this.activeMinSize;
36287         var c2 = Math.max(this.activeMaxSize - size, 0);
36288         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36289             this.dd.resetConstraints();
36290             this.dd.setXConstraint(
36291                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36292                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36293             );
36294             this.dd.setYConstraint(0, 0);
36295         }else{
36296             this.dd.resetConstraints();
36297             this.dd.setXConstraint(0, 0);
36298             this.dd.setYConstraint(
36299                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36300                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36301             );
36302          }
36303         this.dragSpecs.startSize = size;
36304         this.dragSpecs.startPoint = [x, y];
36305         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36306     },
36307     
36308     /** 
36309      * @private Called after the drag operation by the DDProxy
36310      */
36311     onEndProxyDrag : function(e){
36312         Roo.get(this.proxy).setDisplayed(false);
36313         var endPoint = Roo.lib.Event.getXY(e);
36314         if(this.overlay){
36315             this.overlay.hide();
36316         }
36317         var newSize;
36318         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36319             newSize = this.dragSpecs.startSize + 
36320                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36321                     endPoint[0] - this.dragSpecs.startPoint[0] :
36322                     this.dragSpecs.startPoint[0] - endPoint[0]
36323                 );
36324         }else{
36325             newSize = this.dragSpecs.startSize + 
36326                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36327                     endPoint[1] - this.dragSpecs.startPoint[1] :
36328                     this.dragSpecs.startPoint[1] - endPoint[1]
36329                 );
36330         }
36331         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36332         if(newSize != this.dragSpecs.startSize){
36333             if(this.fireEvent('beforeapply', this, newSize) !== false){
36334                 this.adapter.setElementSize(this, newSize);
36335                 this.fireEvent("moved", this, newSize);
36336                 this.fireEvent("resize", this, newSize);
36337             }
36338         }
36339     },
36340     
36341     /**
36342      * Get the adapter this SplitBar uses
36343      * @return The adapter object
36344      */
36345     getAdapter : function(){
36346         return this.adapter;
36347     },
36348     
36349     /**
36350      * Set the adapter this SplitBar uses
36351      * @param {Object} adapter A SplitBar adapter object
36352      */
36353     setAdapter : function(adapter){
36354         this.adapter = adapter;
36355         this.adapter.init(this);
36356     },
36357     
36358     /**
36359      * Gets the minimum size for the resizing element
36360      * @return {Number} The minimum size
36361      */
36362     getMinimumSize : function(){
36363         return this.minSize;
36364     },
36365     
36366     /**
36367      * Sets the minimum size for the resizing element
36368      * @param {Number} minSize The minimum size
36369      */
36370     setMinimumSize : function(minSize){
36371         this.minSize = minSize;
36372     },
36373     
36374     /**
36375      * Gets the maximum size for the resizing element
36376      * @return {Number} The maximum size
36377      */
36378     getMaximumSize : function(){
36379         return this.maxSize;
36380     },
36381     
36382     /**
36383      * Sets the maximum size for the resizing element
36384      * @param {Number} maxSize The maximum size
36385      */
36386     setMaximumSize : function(maxSize){
36387         this.maxSize = maxSize;
36388     },
36389     
36390     /**
36391      * Sets the initialize size for the resizing element
36392      * @param {Number} size The initial size
36393      */
36394     setCurrentSize : function(size){
36395         var oldAnimate = this.animate;
36396         this.animate = false;
36397         this.adapter.setElementSize(this, size);
36398         this.animate = oldAnimate;
36399     },
36400     
36401     /**
36402      * Destroy this splitbar. 
36403      * @param {Boolean} removeEl True to remove the element
36404      */
36405     destroy : function(removeEl){
36406         if(this.shim){
36407             this.shim.remove();
36408         }
36409         this.dd.unreg();
36410         this.proxy.parentNode.removeChild(this.proxy);
36411         if(removeEl){
36412             this.el.remove();
36413         }
36414     }
36415 });
36416
36417 /**
36418  * @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.
36419  */
36420 Roo.bootstrap.SplitBar.createProxy = function(dir){
36421     var proxy = new Roo.Element(document.createElement("div"));
36422     proxy.unselectable();
36423     var cls = 'roo-splitbar-proxy';
36424     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36425     document.body.appendChild(proxy.dom);
36426     return proxy.dom;
36427 };
36428
36429 /** 
36430  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36431  * Default Adapter. It assumes the splitter and resizing element are not positioned
36432  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36433  */
36434 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36435 };
36436
36437 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36438     // do nothing for now
36439     init : function(s){
36440     
36441     },
36442     /**
36443      * Called before drag operations to get the current size of the resizing element. 
36444      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36445      */
36446      getElementSize : function(s){
36447         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36448             return s.resizingEl.getWidth();
36449         }else{
36450             return s.resizingEl.getHeight();
36451         }
36452     },
36453     
36454     /**
36455      * Called after drag operations to set the size of the resizing element.
36456      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36457      * @param {Number} newSize The new size to set
36458      * @param {Function} onComplete A function to be invoked when resizing is complete
36459      */
36460     setElementSize : function(s, newSize, onComplete){
36461         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36462             if(!s.animate){
36463                 s.resizingEl.setWidth(newSize);
36464                 if(onComplete){
36465                     onComplete(s, newSize);
36466                 }
36467             }else{
36468                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36469             }
36470         }else{
36471             
36472             if(!s.animate){
36473                 s.resizingEl.setHeight(newSize);
36474                 if(onComplete){
36475                     onComplete(s, newSize);
36476                 }
36477             }else{
36478                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36479             }
36480         }
36481     }
36482 };
36483
36484 /** 
36485  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36486  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36487  * Adapter that  moves the splitter element to align with the resized sizing element. 
36488  * Used with an absolute positioned SplitBar.
36489  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36490  * document.body, make sure you assign an id to the body element.
36491  */
36492 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36493     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36494     this.container = Roo.get(container);
36495 };
36496
36497 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36498     init : function(s){
36499         this.basic.init(s);
36500     },
36501     
36502     getElementSize : function(s){
36503         return this.basic.getElementSize(s);
36504     },
36505     
36506     setElementSize : function(s, newSize, onComplete){
36507         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36508     },
36509     
36510     moveSplitter : function(s){
36511         var yes = Roo.bootstrap.SplitBar;
36512         switch(s.placement){
36513             case yes.LEFT:
36514                 s.el.setX(s.resizingEl.getRight());
36515                 break;
36516             case yes.RIGHT:
36517                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36518                 break;
36519             case yes.TOP:
36520                 s.el.setY(s.resizingEl.getBottom());
36521                 break;
36522             case yes.BOTTOM:
36523                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36524                 break;
36525         }
36526     }
36527 };
36528
36529 /**
36530  * Orientation constant - Create a vertical SplitBar
36531  * @static
36532  * @type Number
36533  */
36534 Roo.bootstrap.SplitBar.VERTICAL = 1;
36535
36536 /**
36537  * Orientation constant - Create a horizontal SplitBar
36538  * @static
36539  * @type Number
36540  */
36541 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36542
36543 /**
36544  * Placement constant - The resizing element is to the left of the splitter element
36545  * @static
36546  * @type Number
36547  */
36548 Roo.bootstrap.SplitBar.LEFT = 1;
36549
36550 /**
36551  * Placement constant - The resizing element is to the right of the splitter element
36552  * @static
36553  * @type Number
36554  */
36555 Roo.bootstrap.SplitBar.RIGHT = 2;
36556
36557 /**
36558  * Placement constant - The resizing element is positioned above the splitter element
36559  * @static
36560  * @type Number
36561  */
36562 Roo.bootstrap.SplitBar.TOP = 3;
36563
36564 /**
36565  * Placement constant - The resizing element is positioned under splitter element
36566  * @static
36567  * @type Number
36568  */
36569 Roo.bootstrap.SplitBar.BOTTOM = 4;
36570 Roo.namespace("Roo.bootstrap.layout");/*
36571  * Based on:
36572  * Ext JS Library 1.1.1
36573  * Copyright(c) 2006-2007, Ext JS, LLC.
36574  *
36575  * Originally Released Under LGPL - original licence link has changed is not relivant.
36576  *
36577  * Fork - LGPL
36578  * <script type="text/javascript">
36579  */
36580
36581 /**
36582  * @class Roo.bootstrap.layout.Manager
36583  * @extends Roo.bootstrap.Component
36584  * Base class for layout managers.
36585  */
36586 Roo.bootstrap.layout.Manager = function(config)
36587 {
36588     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36589
36590
36591
36592
36593
36594     /** false to disable window resize monitoring @type Boolean */
36595     this.monitorWindowResize = true;
36596     this.regions = {};
36597     this.addEvents({
36598         /**
36599          * @event layout
36600          * Fires when a layout is performed.
36601          * @param {Roo.LayoutManager} this
36602          */
36603         "layout" : true,
36604         /**
36605          * @event regionresized
36606          * Fires when the user resizes a region.
36607          * @param {Roo.LayoutRegion} region The resized region
36608          * @param {Number} newSize The new size (width for east/west, height for north/south)
36609          */
36610         "regionresized" : true,
36611         /**
36612          * @event regioncollapsed
36613          * Fires when a region is collapsed.
36614          * @param {Roo.LayoutRegion} region The collapsed region
36615          */
36616         "regioncollapsed" : true,
36617         /**
36618          * @event regionexpanded
36619          * Fires when a region is expanded.
36620          * @param {Roo.LayoutRegion} region The expanded region
36621          */
36622         "regionexpanded" : true
36623     });
36624     this.updating = false;
36625
36626     if (config.el) {
36627         this.el = Roo.get(config.el);
36628         this.initEvents();
36629     }
36630
36631 };
36632
36633 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36634
36635
36636     regions : null,
36637
36638     monitorWindowResize : true,
36639
36640
36641     updating : false,
36642
36643
36644     onRender : function(ct, position)
36645     {
36646         if(!this.el){
36647             this.el = Roo.get(ct);
36648             this.initEvents();
36649         }
36650         //this.fireEvent('render',this);
36651     },
36652
36653
36654     initEvents: function()
36655     {
36656
36657
36658         // ie scrollbar fix
36659         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36660             document.body.scroll = "no";
36661         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36662             this.el.position('relative');
36663         }
36664         this.id = this.el.id;
36665         this.el.addClass("roo-layout-container");
36666         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36667         if(this.el.dom != document.body ) {
36668             this.el.on('resize', this.layout,this);
36669             this.el.on('show', this.layout,this);
36670         }
36671
36672     },
36673
36674     /**
36675      * Returns true if this layout is currently being updated
36676      * @return {Boolean}
36677      */
36678     isUpdating : function(){
36679         return this.updating;
36680     },
36681
36682     /**
36683      * Suspend the LayoutManager from doing auto-layouts while
36684      * making multiple add or remove calls
36685      */
36686     beginUpdate : function(){
36687         this.updating = true;
36688     },
36689
36690     /**
36691      * Restore auto-layouts and optionally disable the manager from performing a layout
36692      * @param {Boolean} noLayout true to disable a layout update
36693      */
36694     endUpdate : function(noLayout){
36695         this.updating = false;
36696         if(!noLayout){
36697             this.layout();
36698         }
36699     },
36700
36701     layout: function(){
36702         // abstract...
36703     },
36704
36705     onRegionResized : function(region, newSize){
36706         this.fireEvent("regionresized", region, newSize);
36707         this.layout();
36708     },
36709
36710     onRegionCollapsed : function(region){
36711         this.fireEvent("regioncollapsed", region);
36712     },
36713
36714     onRegionExpanded : function(region){
36715         this.fireEvent("regionexpanded", region);
36716     },
36717
36718     /**
36719      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36720      * performs box-model adjustments.
36721      * @return {Object} The size as an object {width: (the width), height: (the height)}
36722      */
36723     getViewSize : function()
36724     {
36725         var size;
36726         if(this.el.dom != document.body){
36727             size = this.el.getSize();
36728         }else{
36729             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36730         }
36731         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36732         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36733         return size;
36734     },
36735
36736     /**
36737      * Returns the Element this layout is bound to.
36738      * @return {Roo.Element}
36739      */
36740     getEl : function(){
36741         return this.el;
36742     },
36743
36744     /**
36745      * Returns the specified region.
36746      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36747      * @return {Roo.LayoutRegion}
36748      */
36749     getRegion : function(target){
36750         return this.regions[target.toLowerCase()];
36751     },
36752
36753     onWindowResize : function(){
36754         if(this.monitorWindowResize){
36755             this.layout();
36756         }
36757     }
36758 });
36759 /*
36760  * Based on:
36761  * Ext JS Library 1.1.1
36762  * Copyright(c) 2006-2007, Ext JS, LLC.
36763  *
36764  * Originally Released Under LGPL - original licence link has changed is not relivant.
36765  *
36766  * Fork - LGPL
36767  * <script type="text/javascript">
36768  */
36769 /**
36770  * @class Roo.bootstrap.layout.Border
36771  * @extends Roo.bootstrap.layout.Manager
36772  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36773  * please see: examples/bootstrap/nested.html<br><br>
36774  
36775 <b>The container the layout is rendered into can be either the body element or any other element.
36776 If it is not the body element, the container needs to either be an absolute positioned element,
36777 or you will need to add "position:relative" to the css of the container.  You will also need to specify
36778 the container size if it is not the body element.</b>
36779
36780 * @constructor
36781 * Create a new Border
36782 * @param {Object} config Configuration options
36783  */
36784 Roo.bootstrap.layout.Border = function(config){
36785     config = config || {};
36786     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
36787     
36788     
36789     
36790     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36791         if(config[region]){
36792             config[region].region = region;
36793             this.addRegion(config[region]);
36794         }
36795     },this);
36796     
36797 };
36798
36799 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
36800
36801 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
36802     
36803     parent : false, // this might point to a 'nest' or a ???
36804     
36805     /**
36806      * Creates and adds a new region if it doesn't already exist.
36807      * @param {String} target The target region key (north, south, east, west or center).
36808      * @param {Object} config The regions config object
36809      * @return {BorderLayoutRegion} The new region
36810      */
36811     addRegion : function(config)
36812     {
36813         if(!this.regions[config.region]){
36814             var r = this.factory(config);
36815             this.bindRegion(r);
36816         }
36817         return this.regions[config.region];
36818     },
36819
36820     // private (kinda)
36821     bindRegion : function(r){
36822         this.regions[r.config.region] = r;
36823         
36824         r.on("visibilitychange",    this.layout, this);
36825         r.on("paneladded",          this.layout, this);
36826         r.on("panelremoved",        this.layout, this);
36827         r.on("invalidated",         this.layout, this);
36828         r.on("resized",             this.onRegionResized, this);
36829         r.on("collapsed",           this.onRegionCollapsed, this);
36830         r.on("expanded",            this.onRegionExpanded, this);
36831     },
36832
36833     /**
36834      * Performs a layout update.
36835      */
36836     layout : function()
36837     {
36838         if(this.updating) {
36839             return;
36840         }
36841         
36842         // render all the rebions if they have not been done alreayd?
36843         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36844             if(this.regions[region] && !this.regions[region].bodyEl){
36845                 this.regions[region].onRender(this.el)
36846             }
36847         },this);
36848         
36849         var size = this.getViewSize();
36850         var w = size.width;
36851         var h = size.height;
36852         var centerW = w;
36853         var centerH = h;
36854         var centerY = 0;
36855         var centerX = 0;
36856         //var x = 0, y = 0;
36857
36858         var rs = this.regions;
36859         var north = rs["north"];
36860         var south = rs["south"]; 
36861         var west = rs["west"];
36862         var east = rs["east"];
36863         var center = rs["center"];
36864         //if(this.hideOnLayout){ // not supported anymore
36865             //c.el.setStyle("display", "none");
36866         //}
36867         if(north && north.isVisible()){
36868             var b = north.getBox();
36869             var m = north.getMargins();
36870             b.width = w - (m.left+m.right);
36871             b.x = m.left;
36872             b.y = m.top;
36873             centerY = b.height + b.y + m.bottom;
36874             centerH -= centerY;
36875             north.updateBox(this.safeBox(b));
36876         }
36877         if(south && south.isVisible()){
36878             var b = south.getBox();
36879             var m = south.getMargins();
36880             b.width = w - (m.left+m.right);
36881             b.x = m.left;
36882             var totalHeight = (b.height + m.top + m.bottom);
36883             b.y = h - totalHeight + m.top;
36884             centerH -= totalHeight;
36885             south.updateBox(this.safeBox(b));
36886         }
36887         if(west && west.isVisible()){
36888             var b = west.getBox();
36889             var m = west.getMargins();
36890             b.height = centerH - (m.top+m.bottom);
36891             b.x = m.left;
36892             b.y = centerY + m.top;
36893             var totalWidth = (b.width + m.left + m.right);
36894             centerX += totalWidth;
36895             centerW -= totalWidth;
36896             west.updateBox(this.safeBox(b));
36897         }
36898         if(east && east.isVisible()){
36899             var b = east.getBox();
36900             var m = east.getMargins();
36901             b.height = centerH - (m.top+m.bottom);
36902             var totalWidth = (b.width + m.left + m.right);
36903             b.x = w - totalWidth + m.left;
36904             b.y = centerY + m.top;
36905             centerW -= totalWidth;
36906             east.updateBox(this.safeBox(b));
36907         }
36908         if(center){
36909             var m = center.getMargins();
36910             var centerBox = {
36911                 x: centerX + m.left,
36912                 y: centerY + m.top,
36913                 width: centerW - (m.left+m.right),
36914                 height: centerH - (m.top+m.bottom)
36915             };
36916             //if(this.hideOnLayout){
36917                 //center.el.setStyle("display", "block");
36918             //}
36919             center.updateBox(this.safeBox(centerBox));
36920         }
36921         this.el.repaint();
36922         this.fireEvent("layout", this);
36923     },
36924
36925     // private
36926     safeBox : function(box){
36927         box.width = Math.max(0, box.width);
36928         box.height = Math.max(0, box.height);
36929         return box;
36930     },
36931
36932     /**
36933      * Adds a ContentPanel (or subclass) to this layout.
36934      * @param {String} target The target region key (north, south, east, west or center).
36935      * @param {Roo.ContentPanel} panel The panel to add
36936      * @return {Roo.ContentPanel} The added panel
36937      */
36938     add : function(target, panel){
36939          
36940         target = target.toLowerCase();
36941         return this.regions[target].add(panel);
36942     },
36943
36944     /**
36945      * Remove a ContentPanel (or subclass) to this layout.
36946      * @param {String} target The target region key (north, south, east, west or center).
36947      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
36948      * @return {Roo.ContentPanel} The removed panel
36949      */
36950     remove : function(target, panel){
36951         target = target.toLowerCase();
36952         return this.regions[target].remove(panel);
36953     },
36954
36955     /**
36956      * Searches all regions for a panel with the specified id
36957      * @param {String} panelId
36958      * @return {Roo.ContentPanel} The panel or null if it wasn't found
36959      */
36960     findPanel : function(panelId){
36961         var rs = this.regions;
36962         for(var target in rs){
36963             if(typeof rs[target] != "function"){
36964                 var p = rs[target].getPanel(panelId);
36965                 if(p){
36966                     return p;
36967                 }
36968             }
36969         }
36970         return null;
36971     },
36972
36973     /**
36974      * Searches all regions for a panel with the specified id and activates (shows) it.
36975      * @param {String/ContentPanel} panelId The panels id or the panel itself
36976      * @return {Roo.ContentPanel} The shown panel or null
36977      */
36978     showPanel : function(panelId) {
36979       var rs = this.regions;
36980       for(var target in rs){
36981          var r = rs[target];
36982          if(typeof r != "function"){
36983             if(r.hasPanel(panelId)){
36984                return r.showPanel(panelId);
36985             }
36986          }
36987       }
36988       return null;
36989    },
36990
36991    /**
36992      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
36993      * @param {Roo.state.Provider} provider (optional) An alternate state provider
36994      */
36995    /*
36996     restoreState : function(provider){
36997         if(!provider){
36998             provider = Roo.state.Manager;
36999         }
37000         var sm = new Roo.LayoutStateManager();
37001         sm.init(this, provider);
37002     },
37003 */
37004  
37005  
37006     /**
37007      * Adds a xtype elements to the layout.
37008      * <pre><code>
37009
37010 layout.addxtype({
37011        xtype : 'ContentPanel',
37012        region: 'west',
37013        items: [ .... ]
37014    }
37015 );
37016
37017 layout.addxtype({
37018         xtype : 'NestedLayoutPanel',
37019         region: 'west',
37020         layout: {
37021            center: { },
37022            west: { }   
37023         },
37024         items : [ ... list of content panels or nested layout panels.. ]
37025    }
37026 );
37027 </code></pre>
37028      * @param {Object} cfg Xtype definition of item to add.
37029      */
37030     addxtype : function(cfg)
37031     {
37032         // basically accepts a pannel...
37033         // can accept a layout region..!?!?
37034         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37035         
37036         
37037         // theory?  children can only be panels??
37038         
37039         //if (!cfg.xtype.match(/Panel$/)) {
37040         //    return false;
37041         //}
37042         var ret = false;
37043         
37044         if (typeof(cfg.region) == 'undefined') {
37045             Roo.log("Failed to add Panel, region was not set");
37046             Roo.log(cfg);
37047             return false;
37048         }
37049         var region = cfg.region;
37050         delete cfg.region;
37051         
37052           
37053         var xitems = [];
37054         if (cfg.items) {
37055             xitems = cfg.items;
37056             delete cfg.items;
37057         }
37058         var nb = false;
37059         
37060         if ( region == 'center') {
37061             Roo.log("Center: " + cfg.title);
37062         }
37063         
37064         
37065         switch(cfg.xtype) 
37066         {
37067             case 'Content':  // ContentPanel (el, cfg)
37068             case 'Scroll':  // ContentPanel (el, cfg)
37069             case 'View': 
37070                 cfg.autoCreate = cfg.autoCreate || true;
37071                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37072                 //} else {
37073                 //    var el = this.el.createChild();
37074                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37075                 //}
37076                 
37077                 this.add(region, ret);
37078                 break;
37079             
37080             /*
37081             case 'TreePanel': // our new panel!
37082                 cfg.el = this.el.createChild();
37083                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37084                 this.add(region, ret);
37085                 break;
37086             */
37087             
37088             case 'Nest': 
37089                 // create a new Layout (which is  a Border Layout...
37090                 
37091                 var clayout = cfg.layout;
37092                 clayout.el  = this.el.createChild();
37093                 clayout.items   = clayout.items  || [];
37094                 
37095                 delete cfg.layout;
37096                 
37097                 // replace this exitems with the clayout ones..
37098                 xitems = clayout.items;
37099                  
37100                 // force background off if it's in center...
37101                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37102                     cfg.background = false;
37103                 }
37104                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37105                 
37106                 
37107                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37108                 //console.log('adding nested layout panel '  + cfg.toSource());
37109                 this.add(region, ret);
37110                 nb = {}; /// find first...
37111                 break;
37112             
37113             case 'Grid':
37114                 
37115                 // needs grid and region
37116                 
37117                 //var el = this.getRegion(region).el.createChild();
37118                 /*
37119                  *var el = this.el.createChild();
37120                 // create the grid first...
37121                 cfg.grid.container = el;
37122                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37123                 */
37124                 
37125                 if (region == 'center' && this.active ) {
37126                     cfg.background = false;
37127                 }
37128                 
37129                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37130                 
37131                 this.add(region, ret);
37132                 /*
37133                 if (cfg.background) {
37134                     // render grid on panel activation (if panel background)
37135                     ret.on('activate', function(gp) {
37136                         if (!gp.grid.rendered) {
37137                     //        gp.grid.render(el);
37138                         }
37139                     });
37140                 } else {
37141                   //  cfg.grid.render(el);
37142                 }
37143                 */
37144                 break;
37145            
37146            
37147             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37148                 // it was the old xcomponent building that caused this before.
37149                 // espeically if border is the top element in the tree.
37150                 ret = this;
37151                 break; 
37152                 
37153                     
37154                 
37155                 
37156                 
37157             default:
37158                 /*
37159                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37160                     
37161                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37162                     this.add(region, ret);
37163                 } else {
37164                 */
37165                     Roo.log(cfg);
37166                     throw "Can not add '" + cfg.xtype + "' to Border";
37167                     return null;
37168              
37169                                 
37170              
37171         }
37172         this.beginUpdate();
37173         // add children..
37174         var region = '';
37175         var abn = {};
37176         Roo.each(xitems, function(i)  {
37177             region = nb && i.region ? i.region : false;
37178             
37179             var add = ret.addxtype(i);
37180            
37181             if (region) {
37182                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37183                 if (!i.background) {
37184                     abn[region] = nb[region] ;
37185                 }
37186             }
37187             
37188         });
37189         this.endUpdate();
37190
37191         // make the last non-background panel active..
37192         //if (nb) { Roo.log(abn); }
37193         if (nb) {
37194             
37195             for(var r in abn) {
37196                 region = this.getRegion(r);
37197                 if (region) {
37198                     // tried using nb[r], but it does not work..
37199                      
37200                     region.showPanel(abn[r]);
37201                    
37202                 }
37203             }
37204         }
37205         return ret;
37206         
37207     },
37208     
37209     
37210 // private
37211     factory : function(cfg)
37212     {
37213         
37214         var validRegions = Roo.bootstrap.layout.Border.regions;
37215
37216         var target = cfg.region;
37217         cfg.mgr = this;
37218         
37219         var r = Roo.bootstrap.layout;
37220         Roo.log(target);
37221         switch(target){
37222             case "north":
37223                 return new r.North(cfg);
37224             case "south":
37225                 return new r.South(cfg);
37226             case "east":
37227                 return new r.East(cfg);
37228             case "west":
37229                 return new r.West(cfg);
37230             case "center":
37231                 return new r.Center(cfg);
37232         }
37233         throw 'Layout region "'+target+'" not supported.';
37234     }
37235     
37236     
37237 });
37238  /*
37239  * Based on:
37240  * Ext JS Library 1.1.1
37241  * Copyright(c) 2006-2007, Ext JS, LLC.
37242  *
37243  * Originally Released Under LGPL - original licence link has changed is not relivant.
37244  *
37245  * Fork - LGPL
37246  * <script type="text/javascript">
37247  */
37248  
37249 /**
37250  * @class Roo.bootstrap.layout.Basic
37251  * @extends Roo.util.Observable
37252  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37253  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37254  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37255  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37256  * @cfg {string}   region  the region that it inhabits..
37257  * @cfg {bool}   skipConfig skip config?
37258  * 
37259
37260  */
37261 Roo.bootstrap.layout.Basic = function(config){
37262     
37263     this.mgr = config.mgr;
37264     
37265     this.position = config.region;
37266     
37267     var skipConfig = config.skipConfig;
37268     
37269     this.events = {
37270         /**
37271          * @scope Roo.BasicLayoutRegion
37272          */
37273         
37274         /**
37275          * @event beforeremove
37276          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37277          * @param {Roo.LayoutRegion} this
37278          * @param {Roo.ContentPanel} panel The panel
37279          * @param {Object} e The cancel event object
37280          */
37281         "beforeremove" : true,
37282         /**
37283          * @event invalidated
37284          * Fires when the layout for this region is changed.
37285          * @param {Roo.LayoutRegion} this
37286          */
37287         "invalidated" : true,
37288         /**
37289          * @event visibilitychange
37290          * Fires when this region is shown or hidden 
37291          * @param {Roo.LayoutRegion} this
37292          * @param {Boolean} visibility true or false
37293          */
37294         "visibilitychange" : true,
37295         /**
37296          * @event paneladded
37297          * Fires when a panel is added. 
37298          * @param {Roo.LayoutRegion} this
37299          * @param {Roo.ContentPanel} panel The panel
37300          */
37301         "paneladded" : true,
37302         /**
37303          * @event panelremoved
37304          * Fires when a panel is removed. 
37305          * @param {Roo.LayoutRegion} this
37306          * @param {Roo.ContentPanel} panel The panel
37307          */
37308         "panelremoved" : true,
37309         /**
37310          * @event beforecollapse
37311          * Fires when this region before collapse.
37312          * @param {Roo.LayoutRegion} this
37313          */
37314         "beforecollapse" : true,
37315         /**
37316          * @event collapsed
37317          * Fires when this region is collapsed.
37318          * @param {Roo.LayoutRegion} this
37319          */
37320         "collapsed" : true,
37321         /**
37322          * @event expanded
37323          * Fires when this region is expanded.
37324          * @param {Roo.LayoutRegion} this
37325          */
37326         "expanded" : true,
37327         /**
37328          * @event slideshow
37329          * Fires when this region is slid into view.
37330          * @param {Roo.LayoutRegion} this
37331          */
37332         "slideshow" : true,
37333         /**
37334          * @event slidehide
37335          * Fires when this region slides out of view. 
37336          * @param {Roo.LayoutRegion} this
37337          */
37338         "slidehide" : true,
37339         /**
37340          * @event panelactivated
37341          * Fires when a panel is activated. 
37342          * @param {Roo.LayoutRegion} this
37343          * @param {Roo.ContentPanel} panel The activated panel
37344          */
37345         "panelactivated" : true,
37346         /**
37347          * @event resized
37348          * Fires when the user resizes this region. 
37349          * @param {Roo.LayoutRegion} this
37350          * @param {Number} newSize The new size (width for east/west, height for north/south)
37351          */
37352         "resized" : true
37353     };
37354     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37355     this.panels = new Roo.util.MixedCollection();
37356     this.panels.getKey = this.getPanelId.createDelegate(this);
37357     this.box = null;
37358     this.activePanel = null;
37359     // ensure listeners are added...
37360     
37361     if (config.listeners || config.events) {
37362         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37363             listeners : config.listeners || {},
37364             events : config.events || {}
37365         });
37366     }
37367     
37368     if(skipConfig !== true){
37369         this.applyConfig(config);
37370     }
37371 };
37372
37373 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37374 {
37375     getPanelId : function(p){
37376         return p.getId();
37377     },
37378     
37379     applyConfig : function(config){
37380         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37381         this.config = config;
37382         
37383     },
37384     
37385     /**
37386      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37387      * the width, for horizontal (north, south) the height.
37388      * @param {Number} newSize The new width or height
37389      */
37390     resizeTo : function(newSize){
37391         var el = this.el ? this.el :
37392                  (this.activePanel ? this.activePanel.getEl() : null);
37393         if(el){
37394             switch(this.position){
37395                 case "east":
37396                 case "west":
37397                     el.setWidth(newSize);
37398                     this.fireEvent("resized", this, newSize);
37399                 break;
37400                 case "north":
37401                 case "south":
37402                     el.setHeight(newSize);
37403                     this.fireEvent("resized", this, newSize);
37404                 break;                
37405             }
37406         }
37407     },
37408     
37409     getBox : function(){
37410         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37411     },
37412     
37413     getMargins : function(){
37414         return this.margins;
37415     },
37416     
37417     updateBox : function(box){
37418         this.box = box;
37419         var el = this.activePanel.getEl();
37420         el.dom.style.left = box.x + "px";
37421         el.dom.style.top = box.y + "px";
37422         this.activePanel.setSize(box.width, box.height);
37423     },
37424     
37425     /**
37426      * Returns the container element for this region.
37427      * @return {Roo.Element}
37428      */
37429     getEl : function(){
37430         return this.activePanel;
37431     },
37432     
37433     /**
37434      * Returns true if this region is currently visible.
37435      * @return {Boolean}
37436      */
37437     isVisible : function(){
37438         return this.activePanel ? true : false;
37439     },
37440     
37441     setActivePanel : function(panel){
37442         panel = this.getPanel(panel);
37443         if(this.activePanel && this.activePanel != panel){
37444             this.activePanel.setActiveState(false);
37445             this.activePanel.getEl().setLeftTop(-10000,-10000);
37446         }
37447         this.activePanel = panel;
37448         panel.setActiveState(true);
37449         if(this.box){
37450             panel.setSize(this.box.width, this.box.height);
37451         }
37452         this.fireEvent("panelactivated", this, panel);
37453         this.fireEvent("invalidated");
37454     },
37455     
37456     /**
37457      * Show the specified panel.
37458      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37459      * @return {Roo.ContentPanel} The shown panel or null
37460      */
37461     showPanel : function(panel){
37462         panel = this.getPanel(panel);
37463         if(panel){
37464             this.setActivePanel(panel);
37465         }
37466         return panel;
37467     },
37468     
37469     /**
37470      * Get the active panel for this region.
37471      * @return {Roo.ContentPanel} The active panel or null
37472      */
37473     getActivePanel : function(){
37474         return this.activePanel;
37475     },
37476     
37477     /**
37478      * Add the passed ContentPanel(s)
37479      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37480      * @return {Roo.ContentPanel} The panel added (if only one was added)
37481      */
37482     add : function(panel){
37483         if(arguments.length > 1){
37484             for(var i = 0, len = arguments.length; i < len; i++) {
37485                 this.add(arguments[i]);
37486             }
37487             return null;
37488         }
37489         if(this.hasPanel(panel)){
37490             this.showPanel(panel);
37491             return panel;
37492         }
37493         var el = panel.getEl();
37494         if(el.dom.parentNode != this.mgr.el.dom){
37495             this.mgr.el.dom.appendChild(el.dom);
37496         }
37497         if(panel.setRegion){
37498             panel.setRegion(this);
37499         }
37500         this.panels.add(panel);
37501         el.setStyle("position", "absolute");
37502         if(!panel.background){
37503             this.setActivePanel(panel);
37504             if(this.config.initialSize && this.panels.getCount()==1){
37505                 this.resizeTo(this.config.initialSize);
37506             }
37507         }
37508         this.fireEvent("paneladded", this, panel);
37509         return panel;
37510     },
37511     
37512     /**
37513      * Returns true if the panel is in this region.
37514      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37515      * @return {Boolean}
37516      */
37517     hasPanel : function(panel){
37518         if(typeof panel == "object"){ // must be panel obj
37519             panel = panel.getId();
37520         }
37521         return this.getPanel(panel) ? true : false;
37522     },
37523     
37524     /**
37525      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37526      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37527      * @param {Boolean} preservePanel Overrides the config preservePanel option
37528      * @return {Roo.ContentPanel} The panel that was removed
37529      */
37530     remove : function(panel, preservePanel){
37531         panel = this.getPanel(panel);
37532         if(!panel){
37533             return null;
37534         }
37535         var e = {};
37536         this.fireEvent("beforeremove", this, panel, e);
37537         if(e.cancel === true){
37538             return null;
37539         }
37540         var panelId = panel.getId();
37541         this.panels.removeKey(panelId);
37542         return panel;
37543     },
37544     
37545     /**
37546      * Returns the panel specified or null if it's not in this region.
37547      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37548      * @return {Roo.ContentPanel}
37549      */
37550     getPanel : function(id){
37551         if(typeof id == "object"){ // must be panel obj
37552             return id;
37553         }
37554         return this.panels.get(id);
37555     },
37556     
37557     /**
37558      * Returns this regions position (north/south/east/west/center).
37559      * @return {String} 
37560      */
37561     getPosition: function(){
37562         return this.position;    
37563     }
37564 });/*
37565  * Based on:
37566  * Ext JS Library 1.1.1
37567  * Copyright(c) 2006-2007, Ext JS, LLC.
37568  *
37569  * Originally Released Under LGPL - original licence link has changed is not relivant.
37570  *
37571  * Fork - LGPL
37572  * <script type="text/javascript">
37573  */
37574  
37575 /**
37576  * @class Roo.bootstrap.layout.Region
37577  * @extends Roo.bootstrap.layout.Basic
37578  * This class represents a region in a layout manager.
37579  
37580  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37581  * @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})
37582  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
37583  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
37584  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
37585  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
37586  * @cfg {String}    title           The title for the region (overrides panel titles)
37587  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
37588  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37589  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
37590  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37591  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
37592  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37593  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
37594  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
37595  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
37596  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
37597
37598  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
37599  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
37600  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
37601  * @cfg {Number}    width           For East/West panels
37602  * @cfg {Number}    height          For North/South panels
37603  * @cfg {Boolean}   split           To show the splitter
37604  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
37605  * 
37606  * @cfg {string}   cls             Extra CSS classes to add to region
37607  * 
37608  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37609  * @cfg {string}   region  the region that it inhabits..
37610  *
37611
37612  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
37613  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
37614
37615  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
37616  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
37617  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
37618  */
37619 Roo.bootstrap.layout.Region = function(config)
37620 {
37621     this.applyConfig(config);
37622
37623     var mgr = config.mgr;
37624     var pos = config.region;
37625     config.skipConfig = true;
37626     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37627     
37628     if (mgr.el) {
37629         this.onRender(mgr.el);   
37630     }
37631      
37632     this.visible = true;
37633     this.collapsed = false;
37634     this.unrendered_panels = [];
37635 };
37636
37637 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37638
37639     position: '', // set by wrapper (eg. north/south etc..)
37640     unrendered_panels : null,  // unrendered panels.
37641     
37642     tabPosition : false,
37643     
37644     mgr: false, // points to 'Border'
37645     
37646     
37647     createBody : function(){
37648         /** This region's body element 
37649         * @type Roo.Element */
37650         this.bodyEl = this.el.createChild({
37651                 tag: "div",
37652                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37653         });
37654     },
37655
37656     onRender: function(ctr, pos)
37657     {
37658         var dh = Roo.DomHelper;
37659         /** This region's container element 
37660         * @type Roo.Element */
37661         this.el = dh.append(ctr.dom, {
37662                 tag: "div",
37663                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37664             }, true);
37665         /** This region's title element 
37666         * @type Roo.Element */
37667     
37668         this.titleEl = dh.append(this.el.dom,  {
37669                 tag: "div",
37670                 unselectable: "on",
37671                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37672                 children:[
37673                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
37674                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37675                 ]
37676             }, true);
37677         
37678         this.titleEl.enableDisplayMode();
37679         /** This region's title text element 
37680         * @type HTMLElement */
37681         this.titleTextEl = this.titleEl.dom.firstChild;
37682         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37683         /*
37684         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37685         this.closeBtn.enableDisplayMode();
37686         this.closeBtn.on("click", this.closeClicked, this);
37687         this.closeBtn.hide();
37688     */
37689         this.createBody(this.config);
37690         if(this.config.hideWhenEmpty){
37691             this.hide();
37692             this.on("paneladded", this.validateVisibility, this);
37693             this.on("panelremoved", this.validateVisibility, this);
37694         }
37695         if(this.autoScroll){
37696             this.bodyEl.setStyle("overflow", "auto");
37697         }else{
37698             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37699         }
37700         //if(c.titlebar !== false){
37701             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37702                 this.titleEl.hide();
37703             }else{
37704                 this.titleEl.show();
37705                 if(this.config.title){
37706                     this.titleTextEl.innerHTML = this.config.title;
37707                 }
37708             }
37709         //}
37710         if(this.config.collapsed){
37711             this.collapse(true);
37712         }
37713         if(this.config.hidden){
37714             this.hide();
37715         }
37716         
37717         if (this.unrendered_panels && this.unrendered_panels.length) {
37718             for (var i =0;i< this.unrendered_panels.length; i++) {
37719                 this.add(this.unrendered_panels[i]);
37720             }
37721             this.unrendered_panels = null;
37722             
37723         }
37724         
37725     },
37726     
37727     applyConfig : function(c)
37728     {
37729         /*
37730          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37731             var dh = Roo.DomHelper;
37732             if(c.titlebar !== false){
37733                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37734                 this.collapseBtn.on("click", this.collapse, this);
37735                 this.collapseBtn.enableDisplayMode();
37736                 /*
37737                 if(c.showPin === true || this.showPin){
37738                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37739                     this.stickBtn.enableDisplayMode();
37740                     this.stickBtn.on("click", this.expand, this);
37741                     this.stickBtn.hide();
37742                 }
37743                 
37744             }
37745             */
37746             /** This region's collapsed element
37747             * @type Roo.Element */
37748             /*
37749              *
37750             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37751                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37752             ]}, true);
37753             
37754             if(c.floatable !== false){
37755                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37756                this.collapsedEl.on("click", this.collapseClick, this);
37757             }
37758
37759             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37760                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37761                    id: "message", unselectable: "on", style:{"float":"left"}});
37762                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37763              }
37764             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37765             this.expandBtn.on("click", this.expand, this);
37766             
37767         }
37768         
37769         if(this.collapseBtn){
37770             this.collapseBtn.setVisible(c.collapsible == true);
37771         }
37772         
37773         this.cmargins = c.cmargins || this.cmargins ||
37774                          (this.position == "west" || this.position == "east" ?
37775                              {top: 0, left: 2, right:2, bottom: 0} :
37776                              {top: 2, left: 0, right:0, bottom: 2});
37777         */
37778         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37779         
37780         
37781         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
37782         
37783         this.autoScroll = c.autoScroll || false;
37784         
37785         
37786        
37787         
37788         this.duration = c.duration || .30;
37789         this.slideDuration = c.slideDuration || .45;
37790         this.config = c;
37791        
37792     },
37793     /**
37794      * Returns true if this region is currently visible.
37795      * @return {Boolean}
37796      */
37797     isVisible : function(){
37798         return this.visible;
37799     },
37800
37801     /**
37802      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
37803      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
37804      */
37805     //setCollapsedTitle : function(title){
37806     //    title = title || "&#160;";
37807      //   if(this.collapsedTitleTextEl){
37808       //      this.collapsedTitleTextEl.innerHTML = title;
37809        // }
37810     //},
37811
37812     getBox : function(){
37813         var b;
37814       //  if(!this.collapsed){
37815             b = this.el.getBox(false, true);
37816        // }else{
37817           //  b = this.collapsedEl.getBox(false, true);
37818         //}
37819         return b;
37820     },
37821
37822     getMargins : function(){
37823         return this.margins;
37824         //return this.collapsed ? this.cmargins : this.margins;
37825     },
37826 /*
37827     highlight : function(){
37828         this.el.addClass("x-layout-panel-dragover");
37829     },
37830
37831     unhighlight : function(){
37832         this.el.removeClass("x-layout-panel-dragover");
37833     },
37834 */
37835     updateBox : function(box)
37836     {
37837         if (!this.bodyEl) {
37838             return; // not rendered yet..
37839         }
37840         
37841         this.box = box;
37842         if(!this.collapsed){
37843             this.el.dom.style.left = box.x + "px";
37844             this.el.dom.style.top = box.y + "px";
37845             this.updateBody(box.width, box.height);
37846         }else{
37847             this.collapsedEl.dom.style.left = box.x + "px";
37848             this.collapsedEl.dom.style.top = box.y + "px";
37849             this.collapsedEl.setSize(box.width, box.height);
37850         }
37851         if(this.tabs){
37852             this.tabs.autoSizeTabs();
37853         }
37854     },
37855
37856     updateBody : function(w, h)
37857     {
37858         if(w !== null){
37859             this.el.setWidth(w);
37860             w -= this.el.getBorderWidth("rl");
37861             if(this.config.adjustments){
37862                 w += this.config.adjustments[0];
37863             }
37864         }
37865         if(h !== null && h > 0){
37866             this.el.setHeight(h);
37867             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
37868             h -= this.el.getBorderWidth("tb");
37869             if(this.config.adjustments){
37870                 h += this.config.adjustments[1];
37871             }
37872             this.bodyEl.setHeight(h);
37873             if(this.tabs){
37874                 h = this.tabs.syncHeight(h);
37875             }
37876         }
37877         if(this.panelSize){
37878             w = w !== null ? w : this.panelSize.width;
37879             h = h !== null ? h : this.panelSize.height;
37880         }
37881         if(this.activePanel){
37882             var el = this.activePanel.getEl();
37883             w = w !== null ? w : el.getWidth();
37884             h = h !== null ? h : el.getHeight();
37885             this.panelSize = {width: w, height: h};
37886             this.activePanel.setSize(w, h);
37887         }
37888         if(Roo.isIE && this.tabs){
37889             this.tabs.el.repaint();
37890         }
37891     },
37892
37893     /**
37894      * Returns the container element for this region.
37895      * @return {Roo.Element}
37896      */
37897     getEl : function(){
37898         return this.el;
37899     },
37900
37901     /**
37902      * Hides this region.
37903      */
37904     hide : function(){
37905         //if(!this.collapsed){
37906             this.el.dom.style.left = "-2000px";
37907             this.el.hide();
37908         //}else{
37909          //   this.collapsedEl.dom.style.left = "-2000px";
37910          //   this.collapsedEl.hide();
37911        // }
37912         this.visible = false;
37913         this.fireEvent("visibilitychange", this, false);
37914     },
37915
37916     /**
37917      * Shows this region if it was previously hidden.
37918      */
37919     show : function(){
37920         //if(!this.collapsed){
37921             this.el.show();
37922         //}else{
37923         //    this.collapsedEl.show();
37924        // }
37925         this.visible = true;
37926         this.fireEvent("visibilitychange", this, true);
37927     },
37928 /*
37929     closeClicked : function(){
37930         if(this.activePanel){
37931             this.remove(this.activePanel);
37932         }
37933     },
37934
37935     collapseClick : function(e){
37936         if(this.isSlid){
37937            e.stopPropagation();
37938            this.slideIn();
37939         }else{
37940            e.stopPropagation();
37941            this.slideOut();
37942         }
37943     },
37944 */
37945     /**
37946      * Collapses this region.
37947      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
37948      */
37949     /*
37950     collapse : function(skipAnim, skipCheck = false){
37951         if(this.collapsed) {
37952             return;
37953         }
37954         
37955         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
37956             
37957             this.collapsed = true;
37958             if(this.split){
37959                 this.split.el.hide();
37960             }
37961             if(this.config.animate && skipAnim !== true){
37962                 this.fireEvent("invalidated", this);
37963                 this.animateCollapse();
37964             }else{
37965                 this.el.setLocation(-20000,-20000);
37966                 this.el.hide();
37967                 this.collapsedEl.show();
37968                 this.fireEvent("collapsed", this);
37969                 this.fireEvent("invalidated", this);
37970             }
37971         }
37972         
37973     },
37974 */
37975     animateCollapse : function(){
37976         // overridden
37977     },
37978
37979     /**
37980      * Expands this region if it was previously collapsed.
37981      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
37982      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
37983      */
37984     /*
37985     expand : function(e, skipAnim){
37986         if(e) {
37987             e.stopPropagation();
37988         }
37989         if(!this.collapsed || this.el.hasActiveFx()) {
37990             return;
37991         }
37992         if(this.isSlid){
37993             this.afterSlideIn();
37994             skipAnim = true;
37995         }
37996         this.collapsed = false;
37997         if(this.config.animate && skipAnim !== true){
37998             this.animateExpand();
37999         }else{
38000             this.el.show();
38001             if(this.split){
38002                 this.split.el.show();
38003             }
38004             this.collapsedEl.setLocation(-2000,-2000);
38005             this.collapsedEl.hide();
38006             this.fireEvent("invalidated", this);
38007             this.fireEvent("expanded", this);
38008         }
38009     },
38010 */
38011     animateExpand : function(){
38012         // overridden
38013     },
38014
38015     initTabs : function()
38016     {
38017         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38018         
38019         var ts = new Roo.bootstrap.panel.Tabs({
38020             el: this.bodyEl.dom,
38021             region : this,
38022             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38023             disableTooltips: this.config.disableTabTips,
38024             toolbar : this.config.toolbar
38025         });
38026         
38027         if(this.config.hideTabs){
38028             ts.stripWrap.setDisplayed(false);
38029         }
38030         this.tabs = ts;
38031         ts.resizeTabs = this.config.resizeTabs === true;
38032         ts.minTabWidth = this.config.minTabWidth || 40;
38033         ts.maxTabWidth = this.config.maxTabWidth || 250;
38034         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38035         ts.monitorResize = false;
38036         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38037         ts.bodyEl.addClass('roo-layout-tabs-body');
38038         this.panels.each(this.initPanelAsTab, this);
38039     },
38040
38041     initPanelAsTab : function(panel){
38042         var ti = this.tabs.addTab(
38043             panel.getEl().id,
38044             panel.getTitle(),
38045             null,
38046             this.config.closeOnTab && panel.isClosable(),
38047             panel.tpl
38048         );
38049         if(panel.tabTip !== undefined){
38050             ti.setTooltip(panel.tabTip);
38051         }
38052         ti.on("activate", function(){
38053               this.setActivePanel(panel);
38054         }, this);
38055         
38056         if(this.config.closeOnTab){
38057             ti.on("beforeclose", function(t, e){
38058                 e.cancel = true;
38059                 this.remove(panel);
38060             }, this);
38061         }
38062         
38063         panel.tabItem = ti;
38064         
38065         return ti;
38066     },
38067
38068     updatePanelTitle : function(panel, title)
38069     {
38070         if(this.activePanel == panel){
38071             this.updateTitle(title);
38072         }
38073         if(this.tabs){
38074             var ti = this.tabs.getTab(panel.getEl().id);
38075             ti.setText(title);
38076             if(panel.tabTip !== undefined){
38077                 ti.setTooltip(panel.tabTip);
38078             }
38079         }
38080     },
38081
38082     updateTitle : function(title){
38083         if(this.titleTextEl && !this.config.title){
38084             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38085         }
38086     },
38087
38088     setActivePanel : function(panel)
38089     {
38090         panel = this.getPanel(panel);
38091         if(this.activePanel && this.activePanel != panel){
38092             if(this.activePanel.setActiveState(false) === false){
38093                 return;
38094             }
38095         }
38096         this.activePanel = panel;
38097         panel.setActiveState(true);
38098         if(this.panelSize){
38099             panel.setSize(this.panelSize.width, this.panelSize.height);
38100         }
38101         if(this.closeBtn){
38102             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38103         }
38104         this.updateTitle(panel.getTitle());
38105         if(this.tabs){
38106             this.fireEvent("invalidated", this);
38107         }
38108         this.fireEvent("panelactivated", this, panel);
38109     },
38110
38111     /**
38112      * Shows the specified panel.
38113      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38114      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38115      */
38116     showPanel : function(panel)
38117     {
38118         panel = this.getPanel(panel);
38119         if(panel){
38120             if(this.tabs){
38121                 var tab = this.tabs.getTab(panel.getEl().id);
38122                 if(tab.isHidden()){
38123                     this.tabs.unhideTab(tab.id);
38124                 }
38125                 tab.activate();
38126             }else{
38127                 this.setActivePanel(panel);
38128             }
38129         }
38130         return panel;
38131     },
38132
38133     /**
38134      * Get the active panel for this region.
38135      * @return {Roo.ContentPanel} The active panel or null
38136      */
38137     getActivePanel : function(){
38138         return this.activePanel;
38139     },
38140
38141     validateVisibility : function(){
38142         if(this.panels.getCount() < 1){
38143             this.updateTitle("&#160;");
38144             this.closeBtn.hide();
38145             this.hide();
38146         }else{
38147             if(!this.isVisible()){
38148                 this.show();
38149             }
38150         }
38151     },
38152
38153     /**
38154      * Adds the passed ContentPanel(s) to this region.
38155      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38156      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38157      */
38158     add : function(panel)
38159     {
38160         if(arguments.length > 1){
38161             for(var i = 0, len = arguments.length; i < len; i++) {
38162                 this.add(arguments[i]);
38163             }
38164             return null;
38165         }
38166         
38167         // if we have not been rendered yet, then we can not really do much of this..
38168         if (!this.bodyEl) {
38169             this.unrendered_panels.push(panel);
38170             return panel;
38171         }
38172         
38173         
38174         
38175         
38176         if(this.hasPanel(panel)){
38177             this.showPanel(panel);
38178             return panel;
38179         }
38180         panel.setRegion(this);
38181         this.panels.add(panel);
38182        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38183             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38184             // and hide them... ???
38185             this.bodyEl.dom.appendChild(panel.getEl().dom);
38186             if(panel.background !== true){
38187                 this.setActivePanel(panel);
38188             }
38189             this.fireEvent("paneladded", this, panel);
38190             return panel;
38191         }
38192         */
38193         if(!this.tabs){
38194             this.initTabs();
38195         }else{
38196             this.initPanelAsTab(panel);
38197         }
38198         
38199         
38200         if(panel.background !== true){
38201             this.tabs.activate(panel.getEl().id);
38202         }
38203         this.fireEvent("paneladded", this, panel);
38204         return panel;
38205     },
38206
38207     /**
38208      * Hides the tab for the specified panel.
38209      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38210      */
38211     hidePanel : function(panel){
38212         if(this.tabs && (panel = this.getPanel(panel))){
38213             this.tabs.hideTab(panel.getEl().id);
38214         }
38215     },
38216
38217     /**
38218      * Unhides the tab for a previously hidden panel.
38219      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38220      */
38221     unhidePanel : function(panel){
38222         if(this.tabs && (panel = this.getPanel(panel))){
38223             this.tabs.unhideTab(panel.getEl().id);
38224         }
38225     },
38226
38227     clearPanels : function(){
38228         while(this.panels.getCount() > 0){
38229              this.remove(this.panels.first());
38230         }
38231     },
38232
38233     /**
38234      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38235      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38236      * @param {Boolean} preservePanel Overrides the config preservePanel option
38237      * @return {Roo.ContentPanel} The panel that was removed
38238      */
38239     remove : function(panel, preservePanel)
38240     {
38241         panel = this.getPanel(panel);
38242         if(!panel){
38243             return null;
38244         }
38245         var e = {};
38246         this.fireEvent("beforeremove", this, panel, e);
38247         if(e.cancel === true){
38248             return null;
38249         }
38250         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38251         var panelId = panel.getId();
38252         this.panels.removeKey(panelId);
38253         if(preservePanel){
38254             document.body.appendChild(panel.getEl().dom);
38255         }
38256         if(this.tabs){
38257             this.tabs.removeTab(panel.getEl().id);
38258         }else if (!preservePanel){
38259             this.bodyEl.dom.removeChild(panel.getEl().dom);
38260         }
38261         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38262             var p = this.panels.first();
38263             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38264             tempEl.appendChild(p.getEl().dom);
38265             this.bodyEl.update("");
38266             this.bodyEl.dom.appendChild(p.getEl().dom);
38267             tempEl = null;
38268             this.updateTitle(p.getTitle());
38269             this.tabs = null;
38270             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38271             this.setActivePanel(p);
38272         }
38273         panel.setRegion(null);
38274         if(this.activePanel == panel){
38275             this.activePanel = null;
38276         }
38277         if(this.config.autoDestroy !== false && preservePanel !== true){
38278             try{panel.destroy();}catch(e){}
38279         }
38280         this.fireEvent("panelremoved", this, panel);
38281         return panel;
38282     },
38283
38284     /**
38285      * Returns the TabPanel component used by this region
38286      * @return {Roo.TabPanel}
38287      */
38288     getTabs : function(){
38289         return this.tabs;
38290     },
38291
38292     createTool : function(parentEl, className){
38293         var btn = Roo.DomHelper.append(parentEl, {
38294             tag: "div",
38295             cls: "x-layout-tools-button",
38296             children: [ {
38297                 tag: "div",
38298                 cls: "roo-layout-tools-button-inner " + className,
38299                 html: "&#160;"
38300             }]
38301         }, true);
38302         btn.addClassOnOver("roo-layout-tools-button-over");
38303         return btn;
38304     }
38305 });/*
38306  * Based on:
38307  * Ext JS Library 1.1.1
38308  * Copyright(c) 2006-2007, Ext JS, LLC.
38309  *
38310  * Originally Released Under LGPL - original licence link has changed is not relivant.
38311  *
38312  * Fork - LGPL
38313  * <script type="text/javascript">
38314  */
38315  
38316
38317
38318 /**
38319  * @class Roo.SplitLayoutRegion
38320  * @extends Roo.LayoutRegion
38321  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38322  */
38323 Roo.bootstrap.layout.Split = function(config){
38324     this.cursor = config.cursor;
38325     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38326 };
38327
38328 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38329 {
38330     splitTip : "Drag to resize.",
38331     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38332     useSplitTips : false,
38333
38334     applyConfig : function(config){
38335         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38336     },
38337     
38338     onRender : function(ctr,pos) {
38339         
38340         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38341         if(!this.config.split){
38342             return;
38343         }
38344         if(!this.split){
38345             
38346             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38347                             tag: "div",
38348                             id: this.el.id + "-split",
38349                             cls: "roo-layout-split roo-layout-split-"+this.position,
38350                             html: "&#160;"
38351             });
38352             /** The SplitBar for this region 
38353             * @type Roo.SplitBar */
38354             // does not exist yet...
38355             Roo.log([this.position, this.orientation]);
38356             
38357             this.split = new Roo.bootstrap.SplitBar({
38358                 dragElement : splitEl,
38359                 resizingElement: this.el,
38360                 orientation : this.orientation
38361             });
38362             
38363             this.split.on("moved", this.onSplitMove, this);
38364             this.split.useShim = this.config.useShim === true;
38365             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38366             if(this.useSplitTips){
38367                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38368             }
38369             //if(config.collapsible){
38370             //    this.split.el.on("dblclick", this.collapse,  this);
38371             //}
38372         }
38373         if(typeof this.config.minSize != "undefined"){
38374             this.split.minSize = this.config.minSize;
38375         }
38376         if(typeof this.config.maxSize != "undefined"){
38377             this.split.maxSize = this.config.maxSize;
38378         }
38379         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38380             this.hideSplitter();
38381         }
38382         
38383     },
38384
38385     getHMaxSize : function(){
38386          var cmax = this.config.maxSize || 10000;
38387          var center = this.mgr.getRegion("center");
38388          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38389     },
38390
38391     getVMaxSize : function(){
38392          var cmax = this.config.maxSize || 10000;
38393          var center = this.mgr.getRegion("center");
38394          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38395     },
38396
38397     onSplitMove : function(split, newSize){
38398         this.fireEvent("resized", this, newSize);
38399     },
38400     
38401     /** 
38402      * Returns the {@link Roo.SplitBar} for this region.
38403      * @return {Roo.SplitBar}
38404      */
38405     getSplitBar : function(){
38406         return this.split;
38407     },
38408     
38409     hide : function(){
38410         this.hideSplitter();
38411         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38412     },
38413
38414     hideSplitter : function(){
38415         if(this.split){
38416             this.split.el.setLocation(-2000,-2000);
38417             this.split.el.hide();
38418         }
38419     },
38420
38421     show : function(){
38422         if(this.split){
38423             this.split.el.show();
38424         }
38425         Roo.bootstrap.layout.Split.superclass.show.call(this);
38426     },
38427     
38428     beforeSlide: function(){
38429         if(Roo.isGecko){// firefox overflow auto bug workaround
38430             this.bodyEl.clip();
38431             if(this.tabs) {
38432                 this.tabs.bodyEl.clip();
38433             }
38434             if(this.activePanel){
38435                 this.activePanel.getEl().clip();
38436                 
38437                 if(this.activePanel.beforeSlide){
38438                     this.activePanel.beforeSlide();
38439                 }
38440             }
38441         }
38442     },
38443     
38444     afterSlide : function(){
38445         if(Roo.isGecko){// firefox overflow auto bug workaround
38446             this.bodyEl.unclip();
38447             if(this.tabs) {
38448                 this.tabs.bodyEl.unclip();
38449             }
38450             if(this.activePanel){
38451                 this.activePanel.getEl().unclip();
38452                 if(this.activePanel.afterSlide){
38453                     this.activePanel.afterSlide();
38454                 }
38455             }
38456         }
38457     },
38458
38459     initAutoHide : function(){
38460         if(this.autoHide !== false){
38461             if(!this.autoHideHd){
38462                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38463                 this.autoHideHd = {
38464                     "mouseout": function(e){
38465                         if(!e.within(this.el, true)){
38466                             st.delay(500);
38467                         }
38468                     },
38469                     "mouseover" : function(e){
38470                         st.cancel();
38471                     },
38472                     scope : this
38473                 };
38474             }
38475             this.el.on(this.autoHideHd);
38476         }
38477     },
38478
38479     clearAutoHide : function(){
38480         if(this.autoHide !== false){
38481             this.el.un("mouseout", this.autoHideHd.mouseout);
38482             this.el.un("mouseover", this.autoHideHd.mouseover);
38483         }
38484     },
38485
38486     clearMonitor : function(){
38487         Roo.get(document).un("click", this.slideInIf, this);
38488     },
38489
38490     // these names are backwards but not changed for compat
38491     slideOut : function(){
38492         if(this.isSlid || this.el.hasActiveFx()){
38493             return;
38494         }
38495         this.isSlid = true;
38496         if(this.collapseBtn){
38497             this.collapseBtn.hide();
38498         }
38499         this.closeBtnState = this.closeBtn.getStyle('display');
38500         this.closeBtn.hide();
38501         if(this.stickBtn){
38502             this.stickBtn.show();
38503         }
38504         this.el.show();
38505         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38506         this.beforeSlide();
38507         this.el.setStyle("z-index", 10001);
38508         this.el.slideIn(this.getSlideAnchor(), {
38509             callback: function(){
38510                 this.afterSlide();
38511                 this.initAutoHide();
38512                 Roo.get(document).on("click", this.slideInIf, this);
38513                 this.fireEvent("slideshow", this);
38514             },
38515             scope: this,
38516             block: true
38517         });
38518     },
38519
38520     afterSlideIn : function(){
38521         this.clearAutoHide();
38522         this.isSlid = false;
38523         this.clearMonitor();
38524         this.el.setStyle("z-index", "");
38525         if(this.collapseBtn){
38526             this.collapseBtn.show();
38527         }
38528         this.closeBtn.setStyle('display', this.closeBtnState);
38529         if(this.stickBtn){
38530             this.stickBtn.hide();
38531         }
38532         this.fireEvent("slidehide", this);
38533     },
38534
38535     slideIn : function(cb){
38536         if(!this.isSlid || this.el.hasActiveFx()){
38537             Roo.callback(cb);
38538             return;
38539         }
38540         this.isSlid = false;
38541         this.beforeSlide();
38542         this.el.slideOut(this.getSlideAnchor(), {
38543             callback: function(){
38544                 this.el.setLeftTop(-10000, -10000);
38545                 this.afterSlide();
38546                 this.afterSlideIn();
38547                 Roo.callback(cb);
38548             },
38549             scope: this,
38550             block: true
38551         });
38552     },
38553     
38554     slideInIf : function(e){
38555         if(!e.within(this.el)){
38556             this.slideIn();
38557         }
38558     },
38559
38560     animateCollapse : function(){
38561         this.beforeSlide();
38562         this.el.setStyle("z-index", 20000);
38563         var anchor = this.getSlideAnchor();
38564         this.el.slideOut(anchor, {
38565             callback : function(){
38566                 this.el.setStyle("z-index", "");
38567                 this.collapsedEl.slideIn(anchor, {duration:.3});
38568                 this.afterSlide();
38569                 this.el.setLocation(-10000,-10000);
38570                 this.el.hide();
38571                 this.fireEvent("collapsed", this);
38572             },
38573             scope: this,
38574             block: true
38575         });
38576     },
38577
38578     animateExpand : function(){
38579         this.beforeSlide();
38580         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38581         this.el.setStyle("z-index", 20000);
38582         this.collapsedEl.hide({
38583             duration:.1
38584         });
38585         this.el.slideIn(this.getSlideAnchor(), {
38586             callback : function(){
38587                 this.el.setStyle("z-index", "");
38588                 this.afterSlide();
38589                 if(this.split){
38590                     this.split.el.show();
38591                 }
38592                 this.fireEvent("invalidated", this);
38593                 this.fireEvent("expanded", this);
38594             },
38595             scope: this,
38596             block: true
38597         });
38598     },
38599
38600     anchors : {
38601         "west" : "left",
38602         "east" : "right",
38603         "north" : "top",
38604         "south" : "bottom"
38605     },
38606
38607     sanchors : {
38608         "west" : "l",
38609         "east" : "r",
38610         "north" : "t",
38611         "south" : "b"
38612     },
38613
38614     canchors : {
38615         "west" : "tl-tr",
38616         "east" : "tr-tl",
38617         "north" : "tl-bl",
38618         "south" : "bl-tl"
38619     },
38620
38621     getAnchor : function(){
38622         return this.anchors[this.position];
38623     },
38624
38625     getCollapseAnchor : function(){
38626         return this.canchors[this.position];
38627     },
38628
38629     getSlideAnchor : function(){
38630         return this.sanchors[this.position];
38631     },
38632
38633     getAlignAdj : function(){
38634         var cm = this.cmargins;
38635         switch(this.position){
38636             case "west":
38637                 return [0, 0];
38638             break;
38639             case "east":
38640                 return [0, 0];
38641             break;
38642             case "north":
38643                 return [0, 0];
38644             break;
38645             case "south":
38646                 return [0, 0];
38647             break;
38648         }
38649     },
38650
38651     getExpandAdj : function(){
38652         var c = this.collapsedEl, cm = this.cmargins;
38653         switch(this.position){
38654             case "west":
38655                 return [-(cm.right+c.getWidth()+cm.left), 0];
38656             break;
38657             case "east":
38658                 return [cm.right+c.getWidth()+cm.left, 0];
38659             break;
38660             case "north":
38661                 return [0, -(cm.top+cm.bottom+c.getHeight())];
38662             break;
38663             case "south":
38664                 return [0, cm.top+cm.bottom+c.getHeight()];
38665             break;
38666         }
38667     }
38668 });/*
38669  * Based on:
38670  * Ext JS Library 1.1.1
38671  * Copyright(c) 2006-2007, Ext JS, LLC.
38672  *
38673  * Originally Released Under LGPL - original licence link has changed is not relivant.
38674  *
38675  * Fork - LGPL
38676  * <script type="text/javascript">
38677  */
38678 /*
38679  * These classes are private internal classes
38680  */
38681 Roo.bootstrap.layout.Center = function(config){
38682     config.region = "center";
38683     Roo.bootstrap.layout.Region.call(this, config);
38684     this.visible = true;
38685     this.minWidth = config.minWidth || 20;
38686     this.minHeight = config.minHeight || 20;
38687 };
38688
38689 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38690     hide : function(){
38691         // center panel can't be hidden
38692     },
38693     
38694     show : function(){
38695         // center panel can't be hidden
38696     },
38697     
38698     getMinWidth: function(){
38699         return this.minWidth;
38700     },
38701     
38702     getMinHeight: function(){
38703         return this.minHeight;
38704     }
38705 });
38706
38707
38708
38709
38710  
38711
38712
38713
38714
38715
38716
38717 Roo.bootstrap.layout.North = function(config)
38718 {
38719     config.region = 'north';
38720     config.cursor = 'n-resize';
38721     
38722     Roo.bootstrap.layout.Split.call(this, config);
38723     
38724     
38725     if(this.split){
38726         this.split.placement = Roo.bootstrap.SplitBar.TOP;
38727         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38728         this.split.el.addClass("roo-layout-split-v");
38729     }
38730     var size = config.initialSize || config.height;
38731     if(typeof size != "undefined"){
38732         this.el.setHeight(size);
38733     }
38734 };
38735 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38736 {
38737     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38738     
38739     
38740     
38741     getBox : function(){
38742         if(this.collapsed){
38743             return this.collapsedEl.getBox();
38744         }
38745         var box = this.el.getBox();
38746         if(this.split){
38747             box.height += this.split.el.getHeight();
38748         }
38749         return box;
38750     },
38751     
38752     updateBox : function(box){
38753         if(this.split && !this.collapsed){
38754             box.height -= this.split.el.getHeight();
38755             this.split.el.setLeft(box.x);
38756             this.split.el.setTop(box.y+box.height);
38757             this.split.el.setWidth(box.width);
38758         }
38759         if(this.collapsed){
38760             this.updateBody(box.width, null);
38761         }
38762         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38763     }
38764 });
38765
38766
38767
38768
38769
38770 Roo.bootstrap.layout.South = function(config){
38771     config.region = 'south';
38772     config.cursor = 's-resize';
38773     Roo.bootstrap.layout.Split.call(this, config);
38774     if(this.split){
38775         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38776         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38777         this.split.el.addClass("roo-layout-split-v");
38778     }
38779     var size = config.initialSize || config.height;
38780     if(typeof size != "undefined"){
38781         this.el.setHeight(size);
38782     }
38783 };
38784
38785 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
38786     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38787     getBox : function(){
38788         if(this.collapsed){
38789             return this.collapsedEl.getBox();
38790         }
38791         var box = this.el.getBox();
38792         if(this.split){
38793             var sh = this.split.el.getHeight();
38794             box.height += sh;
38795             box.y -= sh;
38796         }
38797         return box;
38798     },
38799     
38800     updateBox : function(box){
38801         if(this.split && !this.collapsed){
38802             var sh = this.split.el.getHeight();
38803             box.height -= sh;
38804             box.y += sh;
38805             this.split.el.setLeft(box.x);
38806             this.split.el.setTop(box.y-sh);
38807             this.split.el.setWidth(box.width);
38808         }
38809         if(this.collapsed){
38810             this.updateBody(box.width, null);
38811         }
38812         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38813     }
38814 });
38815
38816 Roo.bootstrap.layout.East = function(config){
38817     config.region = "east";
38818     config.cursor = "e-resize";
38819     Roo.bootstrap.layout.Split.call(this, config);
38820     if(this.split){
38821         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
38822         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38823         this.split.el.addClass("roo-layout-split-h");
38824     }
38825     var size = config.initialSize || config.width;
38826     if(typeof size != "undefined"){
38827         this.el.setWidth(size);
38828     }
38829 };
38830 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
38831     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38832     getBox : function(){
38833         if(this.collapsed){
38834             return this.collapsedEl.getBox();
38835         }
38836         var box = this.el.getBox();
38837         if(this.split){
38838             var sw = this.split.el.getWidth();
38839             box.width += sw;
38840             box.x -= sw;
38841         }
38842         return box;
38843     },
38844
38845     updateBox : function(box){
38846         if(this.split && !this.collapsed){
38847             var sw = this.split.el.getWidth();
38848             box.width -= sw;
38849             this.split.el.setLeft(box.x);
38850             this.split.el.setTop(box.y);
38851             this.split.el.setHeight(box.height);
38852             box.x += sw;
38853         }
38854         if(this.collapsed){
38855             this.updateBody(null, box.height);
38856         }
38857         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38858     }
38859 });
38860
38861 Roo.bootstrap.layout.West = function(config){
38862     config.region = "west";
38863     config.cursor = "w-resize";
38864     
38865     Roo.bootstrap.layout.Split.call(this, config);
38866     if(this.split){
38867         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
38868         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38869         this.split.el.addClass("roo-layout-split-h");
38870     }
38871     
38872 };
38873 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
38874     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38875     
38876     onRender: function(ctr, pos)
38877     {
38878         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
38879         var size = this.config.initialSize || this.config.width;
38880         if(typeof size != "undefined"){
38881             this.el.setWidth(size);
38882         }
38883     },
38884     
38885     getBox : function(){
38886         if(this.collapsed){
38887             return this.collapsedEl.getBox();
38888         }
38889         var box = this.el.getBox();
38890         if(this.split){
38891             box.width += this.split.el.getWidth();
38892         }
38893         return box;
38894     },
38895     
38896     updateBox : function(box){
38897         if(this.split && !this.collapsed){
38898             var sw = this.split.el.getWidth();
38899             box.width -= sw;
38900             this.split.el.setLeft(box.x+box.width);
38901             this.split.el.setTop(box.y);
38902             this.split.el.setHeight(box.height);
38903         }
38904         if(this.collapsed){
38905             this.updateBody(null, box.height);
38906         }
38907         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38908     }
38909 });Roo.namespace("Roo.bootstrap.panel");/*
38910  * Based on:
38911  * Ext JS Library 1.1.1
38912  * Copyright(c) 2006-2007, Ext JS, LLC.
38913  *
38914  * Originally Released Under LGPL - original licence link has changed is not relivant.
38915  *
38916  * Fork - LGPL
38917  * <script type="text/javascript">
38918  */
38919 /**
38920  * @class Roo.ContentPanel
38921  * @extends Roo.util.Observable
38922  * A basic ContentPanel element.
38923  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
38924  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
38925  * @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
38926  * @cfg {Boolean}   closable      True if the panel can be closed/removed
38927  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
38928  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
38929  * @cfg {Toolbar}   toolbar       A toolbar for this panel
38930  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
38931  * @cfg {String} title          The title for this panel
38932  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
38933  * @cfg {String} url            Calls {@link #setUrl} with this value
38934  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
38935  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
38936  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
38937  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
38938  * @cfg {Boolean} badges render the badges
38939
38940  * @constructor
38941  * Create a new ContentPanel.
38942  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
38943  * @param {String/Object} config A string to set only the title or a config object
38944  * @param {String} content (optional) Set the HTML content for this panel
38945  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
38946  */
38947 Roo.bootstrap.panel.Content = function( config){
38948     
38949     this.tpl = config.tpl || false;
38950     
38951     var el = config.el;
38952     var content = config.content;
38953
38954     if(config.autoCreate){ // xtype is available if this is called from factory
38955         el = Roo.id();
38956     }
38957     this.el = Roo.get(el);
38958     if(!this.el && config && config.autoCreate){
38959         if(typeof config.autoCreate == "object"){
38960             if(!config.autoCreate.id){
38961                 config.autoCreate.id = config.id||el;
38962             }
38963             this.el = Roo.DomHelper.append(document.body,
38964                         config.autoCreate, true);
38965         }else{
38966             var elcfg =  {   tag: "div",
38967                             cls: "roo-layout-inactive-content",
38968                             id: config.id||el
38969                             };
38970             if (config.html) {
38971                 elcfg.html = config.html;
38972                 
38973             }
38974                         
38975             this.el = Roo.DomHelper.append(document.body, elcfg , true);
38976         }
38977     } 
38978     this.closable = false;
38979     this.loaded = false;
38980     this.active = false;
38981    
38982       
38983     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
38984         
38985         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
38986         
38987         this.wrapEl = this.el; //this.el.wrap();
38988         var ti = [];
38989         if (config.toolbar.items) {
38990             ti = config.toolbar.items ;
38991             delete config.toolbar.items ;
38992         }
38993         
38994         var nitems = [];
38995         this.toolbar.render(this.wrapEl, 'before');
38996         for(var i =0;i < ti.length;i++) {
38997           //  Roo.log(['add child', items[i]]);
38998             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38999         }
39000         this.toolbar.items = nitems;
39001         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39002         delete config.toolbar;
39003         
39004     }
39005     /*
39006     // xtype created footer. - not sure if will work as we normally have to render first..
39007     if (this.footer && !this.footer.el && this.footer.xtype) {
39008         if (!this.wrapEl) {
39009             this.wrapEl = this.el.wrap();
39010         }
39011     
39012         this.footer.container = this.wrapEl.createChild();
39013          
39014         this.footer = Roo.factory(this.footer, Roo);
39015         
39016     }
39017     */
39018     
39019      if(typeof config == "string"){
39020         this.title = config;
39021     }else{
39022         Roo.apply(this, config);
39023     }
39024     
39025     if(this.resizeEl){
39026         this.resizeEl = Roo.get(this.resizeEl, true);
39027     }else{
39028         this.resizeEl = this.el;
39029     }
39030     // handle view.xtype
39031     
39032  
39033     
39034     
39035     this.addEvents({
39036         /**
39037          * @event activate
39038          * Fires when this panel is activated. 
39039          * @param {Roo.ContentPanel} this
39040          */
39041         "activate" : true,
39042         /**
39043          * @event deactivate
39044          * Fires when this panel is activated. 
39045          * @param {Roo.ContentPanel} this
39046          */
39047         "deactivate" : true,
39048
39049         /**
39050          * @event resize
39051          * Fires when this panel is resized if fitToFrame is true.
39052          * @param {Roo.ContentPanel} this
39053          * @param {Number} width The width after any component adjustments
39054          * @param {Number} height The height after any component adjustments
39055          */
39056         "resize" : true,
39057         
39058          /**
39059          * @event render
39060          * Fires when this tab is created
39061          * @param {Roo.ContentPanel} this
39062          */
39063         "render" : true
39064         
39065         
39066         
39067     });
39068     
39069
39070     
39071     
39072     if(this.autoScroll){
39073         this.resizeEl.setStyle("overflow", "auto");
39074     } else {
39075         // fix randome scrolling
39076         //this.el.on('scroll', function() {
39077         //    Roo.log('fix random scolling');
39078         //    this.scrollTo('top',0); 
39079         //});
39080     }
39081     content = content || this.content;
39082     if(content){
39083         this.setContent(content);
39084     }
39085     if(config && config.url){
39086         this.setUrl(this.url, this.params, this.loadOnce);
39087     }
39088     
39089     
39090     
39091     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39092     
39093     if (this.view && typeof(this.view.xtype) != 'undefined') {
39094         this.view.el = this.el.appendChild(document.createElement("div"));
39095         this.view = Roo.factory(this.view); 
39096         this.view.render  &&  this.view.render(false, '');  
39097     }
39098     
39099     
39100     this.fireEvent('render', this);
39101 };
39102
39103 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39104     
39105     tabTip : '',
39106     
39107     setRegion : function(region){
39108         this.region = region;
39109         this.setActiveClass(region && !this.background);
39110     },
39111     
39112     
39113     setActiveClass: function(state)
39114     {
39115         if(state){
39116            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39117            this.el.setStyle('position','relative');
39118         }else{
39119            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39120            this.el.setStyle('position', 'absolute');
39121         } 
39122     },
39123     
39124     /**
39125      * Returns the toolbar for this Panel if one was configured. 
39126      * @return {Roo.Toolbar} 
39127      */
39128     getToolbar : function(){
39129         return this.toolbar;
39130     },
39131     
39132     setActiveState : function(active)
39133     {
39134         this.active = active;
39135         this.setActiveClass(active);
39136         if(!active){
39137             if(this.fireEvent("deactivate", this) === false){
39138                 return false;
39139             }
39140             return true;
39141         }
39142         this.fireEvent("activate", this);
39143         return true;
39144     },
39145     /**
39146      * Updates this panel's element
39147      * @param {String} content The new content
39148      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39149     */
39150     setContent : function(content, loadScripts){
39151         this.el.update(content, loadScripts);
39152     },
39153
39154     ignoreResize : function(w, h){
39155         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39156             return true;
39157         }else{
39158             this.lastSize = {width: w, height: h};
39159             return false;
39160         }
39161     },
39162     /**
39163      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39164      * @return {Roo.UpdateManager} The UpdateManager
39165      */
39166     getUpdateManager : function(){
39167         return this.el.getUpdateManager();
39168     },
39169      /**
39170      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39171      * @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:
39172 <pre><code>
39173 panel.load({
39174     url: "your-url.php",
39175     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39176     callback: yourFunction,
39177     scope: yourObject, //(optional scope)
39178     discardUrl: false,
39179     nocache: false,
39180     text: "Loading...",
39181     timeout: 30,
39182     scripts: false
39183 });
39184 </code></pre>
39185      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39186      * 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.
39187      * @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}
39188      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39189      * @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.
39190      * @return {Roo.ContentPanel} this
39191      */
39192     load : function(){
39193         var um = this.el.getUpdateManager();
39194         um.update.apply(um, arguments);
39195         return this;
39196     },
39197
39198
39199     /**
39200      * 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.
39201      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39202      * @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)
39203      * @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)
39204      * @return {Roo.UpdateManager} The UpdateManager
39205      */
39206     setUrl : function(url, params, loadOnce){
39207         if(this.refreshDelegate){
39208             this.removeListener("activate", this.refreshDelegate);
39209         }
39210         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39211         this.on("activate", this.refreshDelegate);
39212         return this.el.getUpdateManager();
39213     },
39214     
39215     _handleRefresh : function(url, params, loadOnce){
39216         if(!loadOnce || !this.loaded){
39217             var updater = this.el.getUpdateManager();
39218             updater.update(url, params, this._setLoaded.createDelegate(this));
39219         }
39220     },
39221     
39222     _setLoaded : function(){
39223         this.loaded = true;
39224     }, 
39225     
39226     /**
39227      * Returns this panel's id
39228      * @return {String} 
39229      */
39230     getId : function(){
39231         return this.el.id;
39232     },
39233     
39234     /** 
39235      * Returns this panel's element - used by regiosn to add.
39236      * @return {Roo.Element} 
39237      */
39238     getEl : function(){
39239         return this.wrapEl || this.el;
39240     },
39241     
39242    
39243     
39244     adjustForComponents : function(width, height)
39245     {
39246         //Roo.log('adjustForComponents ');
39247         if(this.resizeEl != this.el){
39248             width -= this.el.getFrameWidth('lr');
39249             height -= this.el.getFrameWidth('tb');
39250         }
39251         if(this.toolbar){
39252             var te = this.toolbar.getEl();
39253             te.setWidth(width);
39254             height -= te.getHeight();
39255         }
39256         if(this.footer){
39257             var te = this.footer.getEl();
39258             te.setWidth(width);
39259             height -= te.getHeight();
39260         }
39261         
39262         
39263         if(this.adjustments){
39264             width += this.adjustments[0];
39265             height += this.adjustments[1];
39266         }
39267         return {"width": width, "height": height};
39268     },
39269     
39270     setSize : function(width, height){
39271         if(this.fitToFrame && !this.ignoreResize(width, height)){
39272             if(this.fitContainer && this.resizeEl != this.el){
39273                 this.el.setSize(width, height);
39274             }
39275             var size = this.adjustForComponents(width, height);
39276             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39277             this.fireEvent('resize', this, size.width, size.height);
39278         }
39279     },
39280     
39281     /**
39282      * Returns this panel's title
39283      * @return {String} 
39284      */
39285     getTitle : function(){
39286         
39287         if (typeof(this.title) != 'object') {
39288             return this.title;
39289         }
39290         
39291         var t = '';
39292         for (var k in this.title) {
39293             if (!this.title.hasOwnProperty(k)) {
39294                 continue;
39295             }
39296             
39297             if (k.indexOf('-') >= 0) {
39298                 var s = k.split('-');
39299                 for (var i = 0; i<s.length; i++) {
39300                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39301                 }
39302             } else {
39303                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39304             }
39305         }
39306         return t;
39307     },
39308     
39309     /**
39310      * Set this panel's title
39311      * @param {String} title
39312      */
39313     setTitle : function(title){
39314         this.title = title;
39315         if(this.region){
39316             this.region.updatePanelTitle(this, title);
39317         }
39318     },
39319     
39320     /**
39321      * Returns true is this panel was configured to be closable
39322      * @return {Boolean} 
39323      */
39324     isClosable : function(){
39325         return this.closable;
39326     },
39327     
39328     beforeSlide : function(){
39329         this.el.clip();
39330         this.resizeEl.clip();
39331     },
39332     
39333     afterSlide : function(){
39334         this.el.unclip();
39335         this.resizeEl.unclip();
39336     },
39337     
39338     /**
39339      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39340      *   Will fail silently if the {@link #setUrl} method has not been called.
39341      *   This does not activate the panel, just updates its content.
39342      */
39343     refresh : function(){
39344         if(this.refreshDelegate){
39345            this.loaded = false;
39346            this.refreshDelegate();
39347         }
39348     },
39349     
39350     /**
39351      * Destroys this panel
39352      */
39353     destroy : function(){
39354         this.el.removeAllListeners();
39355         var tempEl = document.createElement("span");
39356         tempEl.appendChild(this.el.dom);
39357         tempEl.innerHTML = "";
39358         this.el.remove();
39359         this.el = null;
39360     },
39361     
39362     /**
39363      * form - if the content panel contains a form - this is a reference to it.
39364      * @type {Roo.form.Form}
39365      */
39366     form : false,
39367     /**
39368      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39369      *    This contains a reference to it.
39370      * @type {Roo.View}
39371      */
39372     view : false,
39373     
39374       /**
39375      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39376      * <pre><code>
39377
39378 layout.addxtype({
39379        xtype : 'Form',
39380        items: [ .... ]
39381    }
39382 );
39383
39384 </code></pre>
39385      * @param {Object} cfg Xtype definition of item to add.
39386      */
39387     
39388     
39389     getChildContainer: function () {
39390         return this.getEl();
39391     }
39392     
39393     
39394     /*
39395         var  ret = new Roo.factory(cfg);
39396         return ret;
39397         
39398         
39399         // add form..
39400         if (cfg.xtype.match(/^Form$/)) {
39401             
39402             var el;
39403             //if (this.footer) {
39404             //    el = this.footer.container.insertSibling(false, 'before');
39405             //} else {
39406                 el = this.el.createChild();
39407             //}
39408
39409             this.form = new  Roo.form.Form(cfg);
39410             
39411             
39412             if ( this.form.allItems.length) {
39413                 this.form.render(el.dom);
39414             }
39415             return this.form;
39416         }
39417         // should only have one of theses..
39418         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39419             // views.. should not be just added - used named prop 'view''
39420             
39421             cfg.el = this.el.appendChild(document.createElement("div"));
39422             // factory?
39423             
39424             var ret = new Roo.factory(cfg);
39425              
39426              ret.render && ret.render(false, ''); // render blank..
39427             this.view = ret;
39428             return ret;
39429         }
39430         return false;
39431     }
39432     \*/
39433 });
39434  
39435 /**
39436  * @class Roo.bootstrap.panel.Grid
39437  * @extends Roo.bootstrap.panel.Content
39438  * @constructor
39439  * Create a new GridPanel.
39440  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39441  * @param {Object} config A the config object
39442   
39443  */
39444
39445
39446
39447 Roo.bootstrap.panel.Grid = function(config)
39448 {
39449     
39450       
39451     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39452         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39453
39454     config.el = this.wrapper;
39455     //this.el = this.wrapper;
39456     
39457       if (config.container) {
39458         // ctor'ed from a Border/panel.grid
39459         
39460         
39461         this.wrapper.setStyle("overflow", "hidden");
39462         this.wrapper.addClass('roo-grid-container');
39463
39464     }
39465     
39466     
39467     if(config.toolbar){
39468         var tool_el = this.wrapper.createChild();    
39469         this.toolbar = Roo.factory(config.toolbar);
39470         var ti = [];
39471         if (config.toolbar.items) {
39472             ti = config.toolbar.items ;
39473             delete config.toolbar.items ;
39474         }
39475         
39476         var nitems = [];
39477         this.toolbar.render(tool_el);
39478         for(var i =0;i < ti.length;i++) {
39479           //  Roo.log(['add child', items[i]]);
39480             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39481         }
39482         this.toolbar.items = nitems;
39483         
39484         delete config.toolbar;
39485     }
39486     
39487     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39488     config.grid.scrollBody = true;;
39489     config.grid.monitorWindowResize = false; // turn off autosizing
39490     config.grid.autoHeight = false;
39491     config.grid.autoWidth = false;
39492     
39493     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39494     
39495     if (config.background) {
39496         // render grid on panel activation (if panel background)
39497         this.on('activate', function(gp) {
39498             if (!gp.grid.rendered) {
39499                 gp.grid.render(this.wrapper);
39500                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
39501             }
39502         });
39503             
39504     } else {
39505         this.grid.render(this.wrapper);
39506         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
39507
39508     }
39509     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39510     // ??? needed ??? config.el = this.wrapper;
39511     
39512     
39513     
39514   
39515     // xtype created footer. - not sure if will work as we normally have to render first..
39516     if (this.footer && !this.footer.el && this.footer.xtype) {
39517         
39518         var ctr = this.grid.getView().getFooterPanel(true);
39519         this.footer.dataSource = this.grid.dataSource;
39520         this.footer = Roo.factory(this.footer, Roo);
39521         this.footer.render(ctr);
39522         
39523     }
39524     
39525     
39526     
39527     
39528      
39529 };
39530
39531 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39532     getId : function(){
39533         return this.grid.id;
39534     },
39535     
39536     /**
39537      * Returns the grid for this panel
39538      * @return {Roo.bootstrap.Table} 
39539      */
39540     getGrid : function(){
39541         return this.grid;    
39542     },
39543     
39544     setSize : function(width, height){
39545         if(!this.ignoreResize(width, height)){
39546             var grid = this.grid;
39547             var size = this.adjustForComponents(width, height);
39548             var gridel = grid.getGridEl();
39549             gridel.setSize(size.width, size.height);
39550             /*
39551             var thd = grid.getGridEl().select('thead',true).first();
39552             var tbd = grid.getGridEl().select('tbody', true).first();
39553             if (tbd) {
39554                 tbd.setSize(width, height - thd.getHeight());
39555             }
39556             */
39557             grid.autoSize();
39558         }
39559     },
39560      
39561     
39562     
39563     beforeSlide : function(){
39564         this.grid.getView().scroller.clip();
39565     },
39566     
39567     afterSlide : function(){
39568         this.grid.getView().scroller.unclip();
39569     },
39570     
39571     destroy : function(){
39572         this.grid.destroy();
39573         delete this.grid;
39574         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
39575     }
39576 });
39577
39578 /**
39579  * @class Roo.bootstrap.panel.Nest
39580  * @extends Roo.bootstrap.panel.Content
39581  * @constructor
39582  * Create a new Panel, that can contain a layout.Border.
39583  * 
39584  * 
39585  * @param {Roo.BorderLayout} layout The layout for this panel
39586  * @param {String/Object} config A string to set only the title or a config object
39587  */
39588 Roo.bootstrap.panel.Nest = function(config)
39589 {
39590     // construct with only one argument..
39591     /* FIXME - implement nicer consturctors
39592     if (layout.layout) {
39593         config = layout;
39594         layout = config.layout;
39595         delete config.layout;
39596     }
39597     if (layout.xtype && !layout.getEl) {
39598         // then layout needs constructing..
39599         layout = Roo.factory(layout, Roo);
39600     }
39601     */
39602     
39603     config.el =  config.layout.getEl();
39604     
39605     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39606     
39607     config.layout.monitorWindowResize = false; // turn off autosizing
39608     this.layout = config.layout;
39609     this.layout.getEl().addClass("roo-layout-nested-layout");
39610     this.layout.parent = this;
39611     
39612     
39613     
39614     
39615 };
39616
39617 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39618
39619     setSize : function(width, height){
39620         if(!this.ignoreResize(width, height)){
39621             var size = this.adjustForComponents(width, height);
39622             var el = this.layout.getEl();
39623             if (size.height < 1) {
39624                 el.setWidth(size.width);   
39625             } else {
39626                 el.setSize(size.width, size.height);
39627             }
39628             var touch = el.dom.offsetWidth;
39629             this.layout.layout();
39630             // ie requires a double layout on the first pass
39631             if(Roo.isIE && !this.initialized){
39632                 this.initialized = true;
39633                 this.layout.layout();
39634             }
39635         }
39636     },
39637     
39638     // activate all subpanels if not currently active..
39639     
39640     setActiveState : function(active){
39641         this.active = active;
39642         this.setActiveClass(active);
39643         
39644         if(!active){
39645             this.fireEvent("deactivate", this);
39646             return;
39647         }
39648         
39649         this.fireEvent("activate", this);
39650         // not sure if this should happen before or after..
39651         if (!this.layout) {
39652             return; // should not happen..
39653         }
39654         var reg = false;
39655         for (var r in this.layout.regions) {
39656             reg = this.layout.getRegion(r);
39657             if (reg.getActivePanel()) {
39658                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
39659                 reg.setActivePanel(reg.getActivePanel());
39660                 continue;
39661             }
39662             if (!reg.panels.length) {
39663                 continue;
39664             }
39665             reg.showPanel(reg.getPanel(0));
39666         }
39667         
39668         
39669         
39670         
39671     },
39672     
39673     /**
39674      * Returns the nested BorderLayout for this panel
39675      * @return {Roo.BorderLayout} 
39676      */
39677     getLayout : function(){
39678         return this.layout;
39679     },
39680     
39681      /**
39682      * Adds a xtype elements to the layout of the nested panel
39683      * <pre><code>
39684
39685 panel.addxtype({
39686        xtype : 'ContentPanel',
39687        region: 'west',
39688        items: [ .... ]
39689    }
39690 );
39691
39692 panel.addxtype({
39693         xtype : 'NestedLayoutPanel',
39694         region: 'west',
39695         layout: {
39696            center: { },
39697            west: { }   
39698         },
39699         items : [ ... list of content panels or nested layout panels.. ]
39700    }
39701 );
39702 </code></pre>
39703      * @param {Object} cfg Xtype definition of item to add.
39704      */
39705     addxtype : function(cfg) {
39706         return this.layout.addxtype(cfg);
39707     
39708     }
39709 });/*
39710  * Based on:
39711  * Ext JS Library 1.1.1
39712  * Copyright(c) 2006-2007, Ext JS, LLC.
39713  *
39714  * Originally Released Under LGPL - original licence link has changed is not relivant.
39715  *
39716  * Fork - LGPL
39717  * <script type="text/javascript">
39718  */
39719 /**
39720  * @class Roo.TabPanel
39721  * @extends Roo.util.Observable
39722  * A lightweight tab container.
39723  * <br><br>
39724  * Usage:
39725  * <pre><code>
39726 // basic tabs 1, built from existing content
39727 var tabs = new Roo.TabPanel("tabs1");
39728 tabs.addTab("script", "View Script");
39729 tabs.addTab("markup", "View Markup");
39730 tabs.activate("script");
39731
39732 // more advanced tabs, built from javascript
39733 var jtabs = new Roo.TabPanel("jtabs");
39734 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39735
39736 // set up the UpdateManager
39737 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39738 var updater = tab2.getUpdateManager();
39739 updater.setDefaultUrl("ajax1.htm");
39740 tab2.on('activate', updater.refresh, updater, true);
39741
39742 // Use setUrl for Ajax loading
39743 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39744 tab3.setUrl("ajax2.htm", null, true);
39745
39746 // Disabled tab
39747 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39748 tab4.disable();
39749
39750 jtabs.activate("jtabs-1");
39751  * </code></pre>
39752  * @constructor
39753  * Create a new TabPanel.
39754  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39755  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39756  */
39757 Roo.bootstrap.panel.Tabs = function(config){
39758     /**
39759     * The container element for this TabPanel.
39760     * @type Roo.Element
39761     */
39762     this.el = Roo.get(config.el);
39763     delete config.el;
39764     if(config){
39765         if(typeof config == "boolean"){
39766             this.tabPosition = config ? "bottom" : "top";
39767         }else{
39768             Roo.apply(this, config);
39769         }
39770     }
39771     
39772     if(this.tabPosition == "bottom"){
39773         // if tabs are at the bottom = create the body first.
39774         this.bodyEl = Roo.get(this.createBody(this.el.dom));
39775         this.el.addClass("roo-tabs-bottom");
39776     }
39777     // next create the tabs holders
39778     
39779     if (this.tabPosition == "west"){
39780         
39781         var reg = this.region; // fake it..
39782         while (reg) {
39783             if (!reg.mgr.parent) {
39784                 break;
39785             }
39786             reg = reg.mgr.parent.region;
39787         }
39788         Roo.log("got nest?");
39789         Roo.log(reg);
39790         if (reg.mgr.getRegion('west')) {
39791             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
39792             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
39793             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39794             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39795             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39796         
39797             
39798         }
39799         
39800         
39801     } else {
39802      
39803         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
39804         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39805         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39806         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39807     }
39808     
39809     
39810     if(Roo.isIE){
39811         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
39812     }
39813     
39814     // finally - if tabs are at the top, then create the body last..
39815     if(this.tabPosition != "bottom"){
39816         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
39817          * @type Roo.Element
39818          */
39819         this.bodyEl = Roo.get(this.createBody(this.el.dom));
39820         this.el.addClass("roo-tabs-top");
39821     }
39822     this.items = [];
39823
39824     this.bodyEl.setStyle("position", "relative");
39825
39826     this.active = null;
39827     this.activateDelegate = this.activate.createDelegate(this);
39828
39829     this.addEvents({
39830         /**
39831          * @event tabchange
39832          * Fires when the active tab changes
39833          * @param {Roo.TabPanel} this
39834          * @param {Roo.TabPanelItem} activePanel The new active tab
39835          */
39836         "tabchange": true,
39837         /**
39838          * @event beforetabchange
39839          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
39840          * @param {Roo.TabPanel} this
39841          * @param {Object} e Set cancel to true on this object to cancel the tab change
39842          * @param {Roo.TabPanelItem} tab The tab being changed to
39843          */
39844         "beforetabchange" : true
39845     });
39846
39847     Roo.EventManager.onWindowResize(this.onResize, this);
39848     this.cpad = this.el.getPadding("lr");
39849     this.hiddenCount = 0;
39850
39851
39852     // toolbar on the tabbar support...
39853     if (this.toolbar) {
39854         alert("no toolbar support yet");
39855         this.toolbar  = false;
39856         /*
39857         var tcfg = this.toolbar;
39858         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
39859         this.toolbar = new Roo.Toolbar(tcfg);
39860         if (Roo.isSafari) {
39861             var tbl = tcfg.container.child('table', true);
39862             tbl.setAttribute('width', '100%');
39863         }
39864         */
39865         
39866     }
39867    
39868
39869
39870     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
39871 };
39872
39873 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
39874     /*
39875      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
39876      */
39877     tabPosition : "top",
39878     /*
39879      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
39880      */
39881     currentTabWidth : 0,
39882     /*
39883      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
39884      */
39885     minTabWidth : 40,
39886     /*
39887      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
39888      */
39889     maxTabWidth : 250,
39890     /*
39891      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
39892      */
39893     preferredTabWidth : 175,
39894     /*
39895      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
39896      */
39897     resizeTabs : false,
39898     /*
39899      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
39900      */
39901     monitorResize : true,
39902     /*
39903      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
39904      */
39905     toolbar : false,  // set by caller..
39906     
39907     region : false, /// set by caller
39908     
39909     disableTooltips : true, // not used yet...
39910
39911     /**
39912      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
39913      * @param {String} id The id of the div to use <b>or create</b>
39914      * @param {String} text The text for the tab
39915      * @param {String} content (optional) Content to put in the TabPanelItem body
39916      * @param {Boolean} closable (optional) True to create a close icon on the tab
39917      * @return {Roo.TabPanelItem} The created TabPanelItem
39918      */
39919     addTab : function(id, text, content, closable, tpl)
39920     {
39921         var item = new Roo.bootstrap.panel.TabItem({
39922             panel: this,
39923             id : id,
39924             text : text,
39925             closable : closable,
39926             tpl : tpl
39927         });
39928         this.addTabItem(item);
39929         if(content){
39930             item.setContent(content);
39931         }
39932         return item;
39933     },
39934
39935     /**
39936      * Returns the {@link Roo.TabPanelItem} with the specified id/index
39937      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
39938      * @return {Roo.TabPanelItem}
39939      */
39940     getTab : function(id){
39941         return this.items[id];
39942     },
39943
39944     /**
39945      * Hides the {@link Roo.TabPanelItem} with the specified id/index
39946      * @param {String/Number} id The id or index of the TabPanelItem to hide.
39947      */
39948     hideTab : function(id){
39949         var t = this.items[id];
39950         if(!t.isHidden()){
39951            t.setHidden(true);
39952            this.hiddenCount++;
39953            this.autoSizeTabs();
39954         }
39955     },
39956
39957     /**
39958      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
39959      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
39960      */
39961     unhideTab : function(id){
39962         var t = this.items[id];
39963         if(t.isHidden()){
39964            t.setHidden(false);
39965            this.hiddenCount--;
39966            this.autoSizeTabs();
39967         }
39968     },
39969
39970     /**
39971      * Adds an existing {@link Roo.TabPanelItem}.
39972      * @param {Roo.TabPanelItem} item The TabPanelItem to add
39973      */
39974     addTabItem : function(item)
39975     {
39976         this.items[item.id] = item;
39977         this.items.push(item);
39978         this.autoSizeTabs();
39979       //  if(this.resizeTabs){
39980     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
39981   //         this.autoSizeTabs();
39982 //        }else{
39983 //            item.autoSize();
39984        // }
39985     },
39986
39987     /**
39988      * Removes a {@link Roo.TabPanelItem}.
39989      * @param {String/Number} id The id or index of the TabPanelItem to remove.
39990      */
39991     removeTab : function(id){
39992         var items = this.items;
39993         var tab = items[id];
39994         if(!tab) { return; }
39995         var index = items.indexOf(tab);
39996         if(this.active == tab && items.length > 1){
39997             var newTab = this.getNextAvailable(index);
39998             if(newTab) {
39999                 newTab.activate();
40000             }
40001         }
40002         this.stripEl.dom.removeChild(tab.pnode.dom);
40003         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40004             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40005         }
40006         items.splice(index, 1);
40007         delete this.items[tab.id];
40008         tab.fireEvent("close", tab);
40009         tab.purgeListeners();
40010         this.autoSizeTabs();
40011     },
40012
40013     getNextAvailable : function(start){
40014         var items = this.items;
40015         var index = start;
40016         // look for a next tab that will slide over to
40017         // replace the one being removed
40018         while(index < items.length){
40019             var item = items[++index];
40020             if(item && !item.isHidden()){
40021                 return item;
40022             }
40023         }
40024         // if one isn't found select the previous tab (on the left)
40025         index = start;
40026         while(index >= 0){
40027             var item = items[--index];
40028             if(item && !item.isHidden()){
40029                 return item;
40030             }
40031         }
40032         return null;
40033     },
40034
40035     /**
40036      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40037      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40038      */
40039     disableTab : function(id){
40040         var tab = this.items[id];
40041         if(tab && this.active != tab){
40042             tab.disable();
40043         }
40044     },
40045
40046     /**
40047      * Enables a {@link Roo.TabPanelItem} that is disabled.
40048      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40049      */
40050     enableTab : function(id){
40051         var tab = this.items[id];
40052         tab.enable();
40053     },
40054
40055     /**
40056      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40057      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40058      * @return {Roo.TabPanelItem} The TabPanelItem.
40059      */
40060     activate : function(id)
40061     {
40062         //Roo.log('activite:'  + id);
40063         
40064         var tab = this.items[id];
40065         if(!tab){
40066             return null;
40067         }
40068         if(tab == this.active || tab.disabled){
40069             return tab;
40070         }
40071         var e = {};
40072         this.fireEvent("beforetabchange", this, e, tab);
40073         if(e.cancel !== true && !tab.disabled){
40074             if(this.active){
40075                 this.active.hide();
40076             }
40077             this.active = this.items[id];
40078             this.active.show();
40079             this.fireEvent("tabchange", this, this.active);
40080         }
40081         return tab;
40082     },
40083
40084     /**
40085      * Gets the active {@link Roo.TabPanelItem}.
40086      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40087      */
40088     getActiveTab : function(){
40089         return this.active;
40090     },
40091
40092     /**
40093      * Updates the tab body element to fit the height of the container element
40094      * for overflow scrolling
40095      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40096      */
40097     syncHeight : function(targetHeight){
40098         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40099         var bm = this.bodyEl.getMargins();
40100         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40101         this.bodyEl.setHeight(newHeight);
40102         return newHeight;
40103     },
40104
40105     onResize : function(){
40106         if(this.monitorResize){
40107             this.autoSizeTabs();
40108         }
40109     },
40110
40111     /**
40112      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40113      */
40114     beginUpdate : function(){
40115         this.updating = true;
40116     },
40117
40118     /**
40119      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40120      */
40121     endUpdate : function(){
40122         this.updating = false;
40123         this.autoSizeTabs();
40124     },
40125
40126     /**
40127      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40128      */
40129     autoSizeTabs : function()
40130     {
40131         var count = this.items.length;
40132         var vcount = count - this.hiddenCount;
40133         
40134         if (vcount < 2) {
40135             this.stripEl.hide();
40136         } else {
40137             this.stripEl.show();
40138         }
40139         
40140         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40141             return;
40142         }
40143         
40144         
40145         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40146         var availWidth = Math.floor(w / vcount);
40147         var b = this.stripBody;
40148         if(b.getWidth() > w){
40149             var tabs = this.items;
40150             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40151             if(availWidth < this.minTabWidth){
40152                 /*if(!this.sleft){    // incomplete scrolling code
40153                     this.createScrollButtons();
40154                 }
40155                 this.showScroll();
40156                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40157             }
40158         }else{
40159             if(this.currentTabWidth < this.preferredTabWidth){
40160                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40161             }
40162         }
40163     },
40164
40165     /**
40166      * Returns the number of tabs in this TabPanel.
40167      * @return {Number}
40168      */
40169      getCount : function(){
40170          return this.items.length;
40171      },
40172
40173     /**
40174      * Resizes all the tabs to the passed width
40175      * @param {Number} The new width
40176      */
40177     setTabWidth : function(width){
40178         this.currentTabWidth = width;
40179         for(var i = 0, len = this.items.length; i < len; i++) {
40180                 if(!this.items[i].isHidden()) {
40181                 this.items[i].setWidth(width);
40182             }
40183         }
40184     },
40185
40186     /**
40187      * Destroys this TabPanel
40188      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40189      */
40190     destroy : function(removeEl){
40191         Roo.EventManager.removeResizeListener(this.onResize, this);
40192         for(var i = 0, len = this.items.length; i < len; i++){
40193             this.items[i].purgeListeners();
40194         }
40195         if(removeEl === true){
40196             this.el.update("");
40197             this.el.remove();
40198         }
40199     },
40200     
40201     createStrip : function(container)
40202     {
40203         var strip = document.createElement("nav");
40204         strip.className = Roo.bootstrap.version == 4 ?
40205             "navbar-light bg-light" : 
40206             "navbar navbar-default"; //"x-tabs-wrap";
40207         container.appendChild(strip);
40208         return strip;
40209     },
40210     
40211     createStripList : function(strip)
40212     {
40213         // div wrapper for retard IE
40214         // returns the "tr" element.
40215         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40216         //'<div class="x-tabs-strip-wrap">'+
40217           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40218           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40219         return strip.firstChild; //.firstChild.firstChild.firstChild;
40220     },
40221     createBody : function(container)
40222     {
40223         var body = document.createElement("div");
40224         Roo.id(body, "tab-body");
40225         //Roo.fly(body).addClass("x-tabs-body");
40226         Roo.fly(body).addClass("tab-content");
40227         container.appendChild(body);
40228         return body;
40229     },
40230     createItemBody :function(bodyEl, id){
40231         var body = Roo.getDom(id);
40232         if(!body){
40233             body = document.createElement("div");
40234             body.id = id;
40235         }
40236         //Roo.fly(body).addClass("x-tabs-item-body");
40237         Roo.fly(body).addClass("tab-pane");
40238          bodyEl.insertBefore(body, bodyEl.firstChild);
40239         return body;
40240     },
40241     /** @private */
40242     createStripElements :  function(stripEl, text, closable, tpl)
40243     {
40244         var td = document.createElement("li"); // was td..
40245         td.className = 'nav-item';
40246         
40247         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40248         
40249         
40250         stripEl.appendChild(td);
40251         /*if(closable){
40252             td.className = "x-tabs-closable";
40253             if(!this.closeTpl){
40254                 this.closeTpl = new Roo.Template(
40255                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40256                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40257                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40258                 );
40259             }
40260             var el = this.closeTpl.overwrite(td, {"text": text});
40261             var close = el.getElementsByTagName("div")[0];
40262             var inner = el.getElementsByTagName("em")[0];
40263             return {"el": el, "close": close, "inner": inner};
40264         } else {
40265         */
40266         // not sure what this is..
40267 //            if(!this.tabTpl){
40268                 //this.tabTpl = new Roo.Template(
40269                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40270                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40271                 //);
40272 //                this.tabTpl = new Roo.Template(
40273 //                   '<a href="#">' +
40274 //                   '<span unselectable="on"' +
40275 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40276 //                            ' >{text}</span></a>'
40277 //                );
40278 //                
40279 //            }
40280
40281
40282             var template = tpl || this.tabTpl || false;
40283             
40284             if(!template){
40285                 template =  new Roo.Template(
40286                         Roo.bootstrap.version == 4 ? 
40287                             (
40288                                 '<a class="nav-link" href="#" unselectable="on"' +
40289                                      (this.disableTooltips ? '' : ' title="{text}"') +
40290                                      ' >{text}</a>'
40291                             ) : (
40292                                 '<a class="nav-link" href="#">' +
40293                                 '<span unselectable="on"' +
40294                                          (this.disableTooltips ? '' : ' title="{text}"') +
40295                                     ' >{text}</span></a>'
40296                             )
40297                 );
40298             }
40299             
40300             switch (typeof(template)) {
40301                 case 'object' :
40302                     break;
40303                 case 'string' :
40304                     template = new Roo.Template(template);
40305                     break;
40306                 default :
40307                     break;
40308             }
40309             
40310             var el = template.overwrite(td, {"text": text});
40311             
40312             var inner = el.getElementsByTagName("span")[0];
40313             
40314             return {"el": el, "inner": inner};
40315             
40316     }
40317         
40318     
40319 });
40320
40321 /**
40322  * @class Roo.TabPanelItem
40323  * @extends Roo.util.Observable
40324  * Represents an individual item (tab plus body) in a TabPanel.
40325  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40326  * @param {String} id The id of this TabPanelItem
40327  * @param {String} text The text for the tab of this TabPanelItem
40328  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40329  */
40330 Roo.bootstrap.panel.TabItem = function(config){
40331     /**
40332      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40333      * @type Roo.TabPanel
40334      */
40335     this.tabPanel = config.panel;
40336     /**
40337      * The id for this TabPanelItem
40338      * @type String
40339      */
40340     this.id = config.id;
40341     /** @private */
40342     this.disabled = false;
40343     /** @private */
40344     this.text = config.text;
40345     /** @private */
40346     this.loaded = false;
40347     this.closable = config.closable;
40348
40349     /**
40350      * The body element for this TabPanelItem.
40351      * @type Roo.Element
40352      */
40353     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40354     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40355     this.bodyEl.setStyle("display", "block");
40356     this.bodyEl.setStyle("zoom", "1");
40357     //this.hideAction();
40358
40359     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40360     /** @private */
40361     this.el = Roo.get(els.el);
40362     this.inner = Roo.get(els.inner, true);
40363      this.textEl = Roo.bootstrap.version == 4 ?
40364         this.el : Roo.get(this.el.dom.firstChild, true);
40365
40366     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40367     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40368
40369     
40370 //    this.el.on("mousedown", this.onTabMouseDown, this);
40371     this.el.on("click", this.onTabClick, this);
40372     /** @private */
40373     if(config.closable){
40374         var c = Roo.get(els.close, true);
40375         c.dom.title = this.closeText;
40376         c.addClassOnOver("close-over");
40377         c.on("click", this.closeClick, this);
40378      }
40379
40380     this.addEvents({
40381          /**
40382          * @event activate
40383          * Fires when this tab becomes the active tab.
40384          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40385          * @param {Roo.TabPanelItem} this
40386          */
40387         "activate": true,
40388         /**
40389          * @event beforeclose
40390          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40391          * @param {Roo.TabPanelItem} this
40392          * @param {Object} e Set cancel to true on this object to cancel the close.
40393          */
40394         "beforeclose": true,
40395         /**
40396          * @event close
40397          * Fires when this tab is closed.
40398          * @param {Roo.TabPanelItem} this
40399          */
40400          "close": true,
40401         /**
40402          * @event deactivate
40403          * Fires when this tab is no longer the active tab.
40404          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40405          * @param {Roo.TabPanelItem} this
40406          */
40407          "deactivate" : true
40408     });
40409     this.hidden = false;
40410
40411     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40412 };
40413
40414 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40415            {
40416     purgeListeners : function(){
40417        Roo.util.Observable.prototype.purgeListeners.call(this);
40418        this.el.removeAllListeners();
40419     },
40420     /**
40421      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40422      */
40423     show : function(){
40424         this.status_node.addClass("active");
40425         this.showAction();
40426         if(Roo.isOpera){
40427             this.tabPanel.stripWrap.repaint();
40428         }
40429         this.fireEvent("activate", this.tabPanel, this);
40430     },
40431
40432     /**
40433      * Returns true if this tab is the active tab.
40434      * @return {Boolean}
40435      */
40436     isActive : function(){
40437         return this.tabPanel.getActiveTab() == this;
40438     },
40439
40440     /**
40441      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40442      */
40443     hide : function(){
40444         this.status_node.removeClass("active");
40445         this.hideAction();
40446         this.fireEvent("deactivate", this.tabPanel, this);
40447     },
40448
40449     hideAction : function(){
40450         this.bodyEl.hide();
40451         this.bodyEl.setStyle("position", "absolute");
40452         this.bodyEl.setLeft("-20000px");
40453         this.bodyEl.setTop("-20000px");
40454     },
40455
40456     showAction : function(){
40457         this.bodyEl.setStyle("position", "relative");
40458         this.bodyEl.setTop("");
40459         this.bodyEl.setLeft("");
40460         this.bodyEl.show();
40461     },
40462
40463     /**
40464      * Set the tooltip for the tab.
40465      * @param {String} tooltip The tab's tooltip
40466      */
40467     setTooltip : function(text){
40468         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40469             this.textEl.dom.qtip = text;
40470             this.textEl.dom.removeAttribute('title');
40471         }else{
40472             this.textEl.dom.title = text;
40473         }
40474     },
40475
40476     onTabClick : function(e){
40477         e.preventDefault();
40478         this.tabPanel.activate(this.id);
40479     },
40480
40481     onTabMouseDown : function(e){
40482         e.preventDefault();
40483         this.tabPanel.activate(this.id);
40484     },
40485 /*
40486     getWidth : function(){
40487         return this.inner.getWidth();
40488     },
40489
40490     setWidth : function(width){
40491         var iwidth = width - this.linode.getPadding("lr");
40492         this.inner.setWidth(iwidth);
40493         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40494         this.linode.setWidth(width);
40495     },
40496 */
40497     /**
40498      * Show or hide the tab
40499      * @param {Boolean} hidden True to hide or false to show.
40500      */
40501     setHidden : function(hidden){
40502         this.hidden = hidden;
40503         this.linode.setStyle("display", hidden ? "none" : "");
40504     },
40505
40506     /**
40507      * Returns true if this tab is "hidden"
40508      * @return {Boolean}
40509      */
40510     isHidden : function(){
40511         return this.hidden;
40512     },
40513
40514     /**
40515      * Returns the text for this tab
40516      * @return {String}
40517      */
40518     getText : function(){
40519         return this.text;
40520     },
40521     /*
40522     autoSize : function(){
40523         //this.el.beginMeasure();
40524         this.textEl.setWidth(1);
40525         /*
40526          *  #2804 [new] Tabs in Roojs
40527          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40528          */
40529         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40530         //this.el.endMeasure();
40531     //},
40532
40533     /**
40534      * Sets the text for the tab (Note: this also sets the tooltip text)
40535      * @param {String} text The tab's text and tooltip
40536      */
40537     setText : function(text){
40538         this.text = text;
40539         this.textEl.update(text);
40540         this.setTooltip(text);
40541         //if(!this.tabPanel.resizeTabs){
40542         //    this.autoSize();
40543         //}
40544     },
40545     /**
40546      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40547      */
40548     activate : function(){
40549         this.tabPanel.activate(this.id);
40550     },
40551
40552     /**
40553      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40554      */
40555     disable : function(){
40556         if(this.tabPanel.active != this){
40557             this.disabled = true;
40558             this.status_node.addClass("disabled");
40559         }
40560     },
40561
40562     /**
40563      * Enables this TabPanelItem if it was previously disabled.
40564      */
40565     enable : function(){
40566         this.disabled = false;
40567         this.status_node.removeClass("disabled");
40568     },
40569
40570     /**
40571      * Sets the content for this TabPanelItem.
40572      * @param {String} content The content
40573      * @param {Boolean} loadScripts true to look for and load scripts
40574      */
40575     setContent : function(content, loadScripts){
40576         this.bodyEl.update(content, loadScripts);
40577     },
40578
40579     /**
40580      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40581      * @return {Roo.UpdateManager} The UpdateManager
40582      */
40583     getUpdateManager : function(){
40584         return this.bodyEl.getUpdateManager();
40585     },
40586
40587     /**
40588      * Set a URL to be used to load the content for this TabPanelItem.
40589      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40590      * @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)
40591      * @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)
40592      * @return {Roo.UpdateManager} The UpdateManager
40593      */
40594     setUrl : function(url, params, loadOnce){
40595         if(this.refreshDelegate){
40596             this.un('activate', this.refreshDelegate);
40597         }
40598         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40599         this.on("activate", this.refreshDelegate);
40600         return this.bodyEl.getUpdateManager();
40601     },
40602
40603     /** @private */
40604     _handleRefresh : function(url, params, loadOnce){
40605         if(!loadOnce || !this.loaded){
40606             var updater = this.bodyEl.getUpdateManager();
40607             updater.update(url, params, this._setLoaded.createDelegate(this));
40608         }
40609     },
40610
40611     /**
40612      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
40613      *   Will fail silently if the setUrl method has not been called.
40614      *   This does not activate the panel, just updates its content.
40615      */
40616     refresh : function(){
40617         if(this.refreshDelegate){
40618            this.loaded = false;
40619            this.refreshDelegate();
40620         }
40621     },
40622
40623     /** @private */
40624     _setLoaded : function(){
40625         this.loaded = true;
40626     },
40627
40628     /** @private */
40629     closeClick : function(e){
40630         var o = {};
40631         e.stopEvent();
40632         this.fireEvent("beforeclose", this, o);
40633         if(o.cancel !== true){
40634             this.tabPanel.removeTab(this.id);
40635         }
40636     },
40637     /**
40638      * The text displayed in the tooltip for the close icon.
40639      * @type String
40640      */
40641     closeText : "Close this tab"
40642 });
40643 /**
40644 *    This script refer to:
40645 *    Title: International Telephone Input
40646 *    Author: Jack O'Connor
40647 *    Code version:  v12.1.12
40648 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40649 **/
40650
40651 Roo.bootstrap.PhoneInputData = function() {
40652     var d = [
40653       [
40654         "Afghanistan (‫افغانستان‬‎)",
40655         "af",
40656         "93"
40657       ],
40658       [
40659         "Albania (Shqipëri)",
40660         "al",
40661         "355"
40662       ],
40663       [
40664         "Algeria (‫الجزائر‬‎)",
40665         "dz",
40666         "213"
40667       ],
40668       [
40669         "American Samoa",
40670         "as",
40671         "1684"
40672       ],
40673       [
40674         "Andorra",
40675         "ad",
40676         "376"
40677       ],
40678       [
40679         "Angola",
40680         "ao",
40681         "244"
40682       ],
40683       [
40684         "Anguilla",
40685         "ai",
40686         "1264"
40687       ],
40688       [
40689         "Antigua and Barbuda",
40690         "ag",
40691         "1268"
40692       ],
40693       [
40694         "Argentina",
40695         "ar",
40696         "54"
40697       ],
40698       [
40699         "Armenia (Հայաստան)",
40700         "am",
40701         "374"
40702       ],
40703       [
40704         "Aruba",
40705         "aw",
40706         "297"
40707       ],
40708       [
40709         "Australia",
40710         "au",
40711         "61",
40712         0
40713       ],
40714       [
40715         "Austria (Österreich)",
40716         "at",
40717         "43"
40718       ],
40719       [
40720         "Azerbaijan (Azərbaycan)",
40721         "az",
40722         "994"
40723       ],
40724       [
40725         "Bahamas",
40726         "bs",
40727         "1242"
40728       ],
40729       [
40730         "Bahrain (‫البحرين‬‎)",
40731         "bh",
40732         "973"
40733       ],
40734       [
40735         "Bangladesh (বাংলাদেশ)",
40736         "bd",
40737         "880"
40738       ],
40739       [
40740         "Barbados",
40741         "bb",
40742         "1246"
40743       ],
40744       [
40745         "Belarus (Беларусь)",
40746         "by",
40747         "375"
40748       ],
40749       [
40750         "Belgium (België)",
40751         "be",
40752         "32"
40753       ],
40754       [
40755         "Belize",
40756         "bz",
40757         "501"
40758       ],
40759       [
40760         "Benin (Bénin)",
40761         "bj",
40762         "229"
40763       ],
40764       [
40765         "Bermuda",
40766         "bm",
40767         "1441"
40768       ],
40769       [
40770         "Bhutan (འབྲུག)",
40771         "bt",
40772         "975"
40773       ],
40774       [
40775         "Bolivia",
40776         "bo",
40777         "591"
40778       ],
40779       [
40780         "Bosnia and Herzegovina (Босна и Херцеговина)",
40781         "ba",
40782         "387"
40783       ],
40784       [
40785         "Botswana",
40786         "bw",
40787         "267"
40788       ],
40789       [
40790         "Brazil (Brasil)",
40791         "br",
40792         "55"
40793       ],
40794       [
40795         "British Indian Ocean Territory",
40796         "io",
40797         "246"
40798       ],
40799       [
40800         "British Virgin Islands",
40801         "vg",
40802         "1284"
40803       ],
40804       [
40805         "Brunei",
40806         "bn",
40807         "673"
40808       ],
40809       [
40810         "Bulgaria (България)",
40811         "bg",
40812         "359"
40813       ],
40814       [
40815         "Burkina Faso",
40816         "bf",
40817         "226"
40818       ],
40819       [
40820         "Burundi (Uburundi)",
40821         "bi",
40822         "257"
40823       ],
40824       [
40825         "Cambodia (កម្ពុជា)",
40826         "kh",
40827         "855"
40828       ],
40829       [
40830         "Cameroon (Cameroun)",
40831         "cm",
40832         "237"
40833       ],
40834       [
40835         "Canada",
40836         "ca",
40837         "1",
40838         1,
40839         ["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"]
40840       ],
40841       [
40842         "Cape Verde (Kabu Verdi)",
40843         "cv",
40844         "238"
40845       ],
40846       [
40847         "Caribbean Netherlands",
40848         "bq",
40849         "599",
40850         1
40851       ],
40852       [
40853         "Cayman Islands",
40854         "ky",
40855         "1345"
40856       ],
40857       [
40858         "Central African Republic (République centrafricaine)",
40859         "cf",
40860         "236"
40861       ],
40862       [
40863         "Chad (Tchad)",
40864         "td",
40865         "235"
40866       ],
40867       [
40868         "Chile",
40869         "cl",
40870         "56"
40871       ],
40872       [
40873         "China (中国)",
40874         "cn",
40875         "86"
40876       ],
40877       [
40878         "Christmas Island",
40879         "cx",
40880         "61",
40881         2
40882       ],
40883       [
40884         "Cocos (Keeling) Islands",
40885         "cc",
40886         "61",
40887         1
40888       ],
40889       [
40890         "Colombia",
40891         "co",
40892         "57"
40893       ],
40894       [
40895         "Comoros (‫جزر القمر‬‎)",
40896         "km",
40897         "269"
40898       ],
40899       [
40900         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
40901         "cd",
40902         "243"
40903       ],
40904       [
40905         "Congo (Republic) (Congo-Brazzaville)",
40906         "cg",
40907         "242"
40908       ],
40909       [
40910         "Cook Islands",
40911         "ck",
40912         "682"
40913       ],
40914       [
40915         "Costa Rica",
40916         "cr",
40917         "506"
40918       ],
40919       [
40920         "Côte d’Ivoire",
40921         "ci",
40922         "225"
40923       ],
40924       [
40925         "Croatia (Hrvatska)",
40926         "hr",
40927         "385"
40928       ],
40929       [
40930         "Cuba",
40931         "cu",
40932         "53"
40933       ],
40934       [
40935         "Curaçao",
40936         "cw",
40937         "599",
40938         0
40939       ],
40940       [
40941         "Cyprus (Κύπρος)",
40942         "cy",
40943         "357"
40944       ],
40945       [
40946         "Czech Republic (Česká republika)",
40947         "cz",
40948         "420"
40949       ],
40950       [
40951         "Denmark (Danmark)",
40952         "dk",
40953         "45"
40954       ],
40955       [
40956         "Djibouti",
40957         "dj",
40958         "253"
40959       ],
40960       [
40961         "Dominica",
40962         "dm",
40963         "1767"
40964       ],
40965       [
40966         "Dominican Republic (República Dominicana)",
40967         "do",
40968         "1",
40969         2,
40970         ["809", "829", "849"]
40971       ],
40972       [
40973         "Ecuador",
40974         "ec",
40975         "593"
40976       ],
40977       [
40978         "Egypt (‫مصر‬‎)",
40979         "eg",
40980         "20"
40981       ],
40982       [
40983         "El Salvador",
40984         "sv",
40985         "503"
40986       ],
40987       [
40988         "Equatorial Guinea (Guinea Ecuatorial)",
40989         "gq",
40990         "240"
40991       ],
40992       [
40993         "Eritrea",
40994         "er",
40995         "291"
40996       ],
40997       [
40998         "Estonia (Eesti)",
40999         "ee",
41000         "372"
41001       ],
41002       [
41003         "Ethiopia",
41004         "et",
41005         "251"
41006       ],
41007       [
41008         "Falkland Islands (Islas Malvinas)",
41009         "fk",
41010         "500"
41011       ],
41012       [
41013         "Faroe Islands (Føroyar)",
41014         "fo",
41015         "298"
41016       ],
41017       [
41018         "Fiji",
41019         "fj",
41020         "679"
41021       ],
41022       [
41023         "Finland (Suomi)",
41024         "fi",
41025         "358",
41026         0
41027       ],
41028       [
41029         "France",
41030         "fr",
41031         "33"
41032       ],
41033       [
41034         "French Guiana (Guyane française)",
41035         "gf",
41036         "594"
41037       ],
41038       [
41039         "French Polynesia (Polynésie française)",
41040         "pf",
41041         "689"
41042       ],
41043       [
41044         "Gabon",
41045         "ga",
41046         "241"
41047       ],
41048       [
41049         "Gambia",
41050         "gm",
41051         "220"
41052       ],
41053       [
41054         "Georgia (საქართველო)",
41055         "ge",
41056         "995"
41057       ],
41058       [
41059         "Germany (Deutschland)",
41060         "de",
41061         "49"
41062       ],
41063       [
41064         "Ghana (Gaana)",
41065         "gh",
41066         "233"
41067       ],
41068       [
41069         "Gibraltar",
41070         "gi",
41071         "350"
41072       ],
41073       [
41074         "Greece (Ελλάδα)",
41075         "gr",
41076         "30"
41077       ],
41078       [
41079         "Greenland (Kalaallit Nunaat)",
41080         "gl",
41081         "299"
41082       ],
41083       [
41084         "Grenada",
41085         "gd",
41086         "1473"
41087       ],
41088       [
41089         "Guadeloupe",
41090         "gp",
41091         "590",
41092         0
41093       ],
41094       [
41095         "Guam",
41096         "gu",
41097         "1671"
41098       ],
41099       [
41100         "Guatemala",
41101         "gt",
41102         "502"
41103       ],
41104       [
41105         "Guernsey",
41106         "gg",
41107         "44",
41108         1
41109       ],
41110       [
41111         "Guinea (Guinée)",
41112         "gn",
41113         "224"
41114       ],
41115       [
41116         "Guinea-Bissau (Guiné Bissau)",
41117         "gw",
41118         "245"
41119       ],
41120       [
41121         "Guyana",
41122         "gy",
41123         "592"
41124       ],
41125       [
41126         "Haiti",
41127         "ht",
41128         "509"
41129       ],
41130       [
41131         "Honduras",
41132         "hn",
41133         "504"
41134       ],
41135       [
41136         "Hong Kong (香港)",
41137         "hk",
41138         "852"
41139       ],
41140       [
41141         "Hungary (Magyarország)",
41142         "hu",
41143         "36"
41144       ],
41145       [
41146         "Iceland (Ísland)",
41147         "is",
41148         "354"
41149       ],
41150       [
41151         "India (भारत)",
41152         "in",
41153         "91"
41154       ],
41155       [
41156         "Indonesia",
41157         "id",
41158         "62"
41159       ],
41160       [
41161         "Iran (‫ایران‬‎)",
41162         "ir",
41163         "98"
41164       ],
41165       [
41166         "Iraq (‫العراق‬‎)",
41167         "iq",
41168         "964"
41169       ],
41170       [
41171         "Ireland",
41172         "ie",
41173         "353"
41174       ],
41175       [
41176         "Isle of Man",
41177         "im",
41178         "44",
41179         2
41180       ],
41181       [
41182         "Israel (‫ישראל‬‎)",
41183         "il",
41184         "972"
41185       ],
41186       [
41187         "Italy (Italia)",
41188         "it",
41189         "39",
41190         0
41191       ],
41192       [
41193         "Jamaica",
41194         "jm",
41195         "1876"
41196       ],
41197       [
41198         "Japan (日本)",
41199         "jp",
41200         "81"
41201       ],
41202       [
41203         "Jersey",
41204         "je",
41205         "44",
41206         3
41207       ],
41208       [
41209         "Jordan (‫الأردن‬‎)",
41210         "jo",
41211         "962"
41212       ],
41213       [
41214         "Kazakhstan (Казахстан)",
41215         "kz",
41216         "7",
41217         1
41218       ],
41219       [
41220         "Kenya",
41221         "ke",
41222         "254"
41223       ],
41224       [
41225         "Kiribati",
41226         "ki",
41227         "686"
41228       ],
41229       [
41230         "Kosovo",
41231         "xk",
41232         "383"
41233       ],
41234       [
41235         "Kuwait (‫الكويت‬‎)",
41236         "kw",
41237         "965"
41238       ],
41239       [
41240         "Kyrgyzstan (Кыргызстан)",
41241         "kg",
41242         "996"
41243       ],
41244       [
41245         "Laos (ລາວ)",
41246         "la",
41247         "856"
41248       ],
41249       [
41250         "Latvia (Latvija)",
41251         "lv",
41252         "371"
41253       ],
41254       [
41255         "Lebanon (‫لبنان‬‎)",
41256         "lb",
41257         "961"
41258       ],
41259       [
41260         "Lesotho",
41261         "ls",
41262         "266"
41263       ],
41264       [
41265         "Liberia",
41266         "lr",
41267         "231"
41268       ],
41269       [
41270         "Libya (‫ليبيا‬‎)",
41271         "ly",
41272         "218"
41273       ],
41274       [
41275         "Liechtenstein",
41276         "li",
41277         "423"
41278       ],
41279       [
41280         "Lithuania (Lietuva)",
41281         "lt",
41282         "370"
41283       ],
41284       [
41285         "Luxembourg",
41286         "lu",
41287         "352"
41288       ],
41289       [
41290         "Macau (澳門)",
41291         "mo",
41292         "853"
41293       ],
41294       [
41295         "Macedonia (FYROM) (Македонија)",
41296         "mk",
41297         "389"
41298       ],
41299       [
41300         "Madagascar (Madagasikara)",
41301         "mg",
41302         "261"
41303       ],
41304       [
41305         "Malawi",
41306         "mw",
41307         "265"
41308       ],
41309       [
41310         "Malaysia",
41311         "my",
41312         "60"
41313       ],
41314       [
41315         "Maldives",
41316         "mv",
41317         "960"
41318       ],
41319       [
41320         "Mali",
41321         "ml",
41322         "223"
41323       ],
41324       [
41325         "Malta",
41326         "mt",
41327         "356"
41328       ],
41329       [
41330         "Marshall Islands",
41331         "mh",
41332         "692"
41333       ],
41334       [
41335         "Martinique",
41336         "mq",
41337         "596"
41338       ],
41339       [
41340         "Mauritania (‫موريتانيا‬‎)",
41341         "mr",
41342         "222"
41343       ],
41344       [
41345         "Mauritius (Moris)",
41346         "mu",
41347         "230"
41348       ],
41349       [
41350         "Mayotte",
41351         "yt",
41352         "262",
41353         1
41354       ],
41355       [
41356         "Mexico (México)",
41357         "mx",
41358         "52"
41359       ],
41360       [
41361         "Micronesia",
41362         "fm",
41363         "691"
41364       ],
41365       [
41366         "Moldova (Republica Moldova)",
41367         "md",
41368         "373"
41369       ],
41370       [
41371         "Monaco",
41372         "mc",
41373         "377"
41374       ],
41375       [
41376         "Mongolia (Монгол)",
41377         "mn",
41378         "976"
41379       ],
41380       [
41381         "Montenegro (Crna Gora)",
41382         "me",
41383         "382"
41384       ],
41385       [
41386         "Montserrat",
41387         "ms",
41388         "1664"
41389       ],
41390       [
41391         "Morocco (‫المغرب‬‎)",
41392         "ma",
41393         "212",
41394         0
41395       ],
41396       [
41397         "Mozambique (Moçambique)",
41398         "mz",
41399         "258"
41400       ],
41401       [
41402         "Myanmar (Burma) (မြန်မာ)",
41403         "mm",
41404         "95"
41405       ],
41406       [
41407         "Namibia (Namibië)",
41408         "na",
41409         "264"
41410       ],
41411       [
41412         "Nauru",
41413         "nr",
41414         "674"
41415       ],
41416       [
41417         "Nepal (नेपाल)",
41418         "np",
41419         "977"
41420       ],
41421       [
41422         "Netherlands (Nederland)",
41423         "nl",
41424         "31"
41425       ],
41426       [
41427         "New Caledonia (Nouvelle-Calédonie)",
41428         "nc",
41429         "687"
41430       ],
41431       [
41432         "New Zealand",
41433         "nz",
41434         "64"
41435       ],
41436       [
41437         "Nicaragua",
41438         "ni",
41439         "505"
41440       ],
41441       [
41442         "Niger (Nijar)",
41443         "ne",
41444         "227"
41445       ],
41446       [
41447         "Nigeria",
41448         "ng",
41449         "234"
41450       ],
41451       [
41452         "Niue",
41453         "nu",
41454         "683"
41455       ],
41456       [
41457         "Norfolk Island",
41458         "nf",
41459         "672"
41460       ],
41461       [
41462         "North Korea (조선 민주주의 인민 공화국)",
41463         "kp",
41464         "850"
41465       ],
41466       [
41467         "Northern Mariana Islands",
41468         "mp",
41469         "1670"
41470       ],
41471       [
41472         "Norway (Norge)",
41473         "no",
41474         "47",
41475         0
41476       ],
41477       [
41478         "Oman (‫عُمان‬‎)",
41479         "om",
41480         "968"
41481       ],
41482       [
41483         "Pakistan (‫پاکستان‬‎)",
41484         "pk",
41485         "92"
41486       ],
41487       [
41488         "Palau",
41489         "pw",
41490         "680"
41491       ],
41492       [
41493         "Palestine (‫فلسطين‬‎)",
41494         "ps",
41495         "970"
41496       ],
41497       [
41498         "Panama (Panamá)",
41499         "pa",
41500         "507"
41501       ],
41502       [
41503         "Papua New Guinea",
41504         "pg",
41505         "675"
41506       ],
41507       [
41508         "Paraguay",
41509         "py",
41510         "595"
41511       ],
41512       [
41513         "Peru (Perú)",
41514         "pe",
41515         "51"
41516       ],
41517       [
41518         "Philippines",
41519         "ph",
41520         "63"
41521       ],
41522       [
41523         "Poland (Polska)",
41524         "pl",
41525         "48"
41526       ],
41527       [
41528         "Portugal",
41529         "pt",
41530         "351"
41531       ],
41532       [
41533         "Puerto Rico",
41534         "pr",
41535         "1",
41536         3,
41537         ["787", "939"]
41538       ],
41539       [
41540         "Qatar (‫قطر‬‎)",
41541         "qa",
41542         "974"
41543       ],
41544       [
41545         "Réunion (La Réunion)",
41546         "re",
41547         "262",
41548         0
41549       ],
41550       [
41551         "Romania (România)",
41552         "ro",
41553         "40"
41554       ],
41555       [
41556         "Russia (Россия)",
41557         "ru",
41558         "7",
41559         0
41560       ],
41561       [
41562         "Rwanda",
41563         "rw",
41564         "250"
41565       ],
41566       [
41567         "Saint Barthélemy",
41568         "bl",
41569         "590",
41570         1
41571       ],
41572       [
41573         "Saint Helena",
41574         "sh",
41575         "290"
41576       ],
41577       [
41578         "Saint Kitts and Nevis",
41579         "kn",
41580         "1869"
41581       ],
41582       [
41583         "Saint Lucia",
41584         "lc",
41585         "1758"
41586       ],
41587       [
41588         "Saint Martin (Saint-Martin (partie française))",
41589         "mf",
41590         "590",
41591         2
41592       ],
41593       [
41594         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41595         "pm",
41596         "508"
41597       ],
41598       [
41599         "Saint Vincent and the Grenadines",
41600         "vc",
41601         "1784"
41602       ],
41603       [
41604         "Samoa",
41605         "ws",
41606         "685"
41607       ],
41608       [
41609         "San Marino",
41610         "sm",
41611         "378"
41612       ],
41613       [
41614         "São Tomé and Príncipe (São Tomé e Príncipe)",
41615         "st",
41616         "239"
41617       ],
41618       [
41619         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
41620         "sa",
41621         "966"
41622       ],
41623       [
41624         "Senegal (Sénégal)",
41625         "sn",
41626         "221"
41627       ],
41628       [
41629         "Serbia (Србија)",
41630         "rs",
41631         "381"
41632       ],
41633       [
41634         "Seychelles",
41635         "sc",
41636         "248"
41637       ],
41638       [
41639         "Sierra Leone",
41640         "sl",
41641         "232"
41642       ],
41643       [
41644         "Singapore",
41645         "sg",
41646         "65"
41647       ],
41648       [
41649         "Sint Maarten",
41650         "sx",
41651         "1721"
41652       ],
41653       [
41654         "Slovakia (Slovensko)",
41655         "sk",
41656         "421"
41657       ],
41658       [
41659         "Slovenia (Slovenija)",
41660         "si",
41661         "386"
41662       ],
41663       [
41664         "Solomon Islands",
41665         "sb",
41666         "677"
41667       ],
41668       [
41669         "Somalia (Soomaaliya)",
41670         "so",
41671         "252"
41672       ],
41673       [
41674         "South Africa",
41675         "za",
41676         "27"
41677       ],
41678       [
41679         "South Korea (대한민국)",
41680         "kr",
41681         "82"
41682       ],
41683       [
41684         "South Sudan (‫جنوب السودان‬‎)",
41685         "ss",
41686         "211"
41687       ],
41688       [
41689         "Spain (España)",
41690         "es",
41691         "34"
41692       ],
41693       [
41694         "Sri Lanka (ශ්‍රී ලංකාව)",
41695         "lk",
41696         "94"
41697       ],
41698       [
41699         "Sudan (‫السودان‬‎)",
41700         "sd",
41701         "249"
41702       ],
41703       [
41704         "Suriname",
41705         "sr",
41706         "597"
41707       ],
41708       [
41709         "Svalbard and Jan Mayen",
41710         "sj",
41711         "47",
41712         1
41713       ],
41714       [
41715         "Swaziland",
41716         "sz",
41717         "268"
41718       ],
41719       [
41720         "Sweden (Sverige)",
41721         "se",
41722         "46"
41723       ],
41724       [
41725         "Switzerland (Schweiz)",
41726         "ch",
41727         "41"
41728       ],
41729       [
41730         "Syria (‫سوريا‬‎)",
41731         "sy",
41732         "963"
41733       ],
41734       [
41735         "Taiwan (台灣)",
41736         "tw",
41737         "886"
41738       ],
41739       [
41740         "Tajikistan",
41741         "tj",
41742         "992"
41743       ],
41744       [
41745         "Tanzania",
41746         "tz",
41747         "255"
41748       ],
41749       [
41750         "Thailand (ไทย)",
41751         "th",
41752         "66"
41753       ],
41754       [
41755         "Timor-Leste",
41756         "tl",
41757         "670"
41758       ],
41759       [
41760         "Togo",
41761         "tg",
41762         "228"
41763       ],
41764       [
41765         "Tokelau",
41766         "tk",
41767         "690"
41768       ],
41769       [
41770         "Tonga",
41771         "to",
41772         "676"
41773       ],
41774       [
41775         "Trinidad and Tobago",
41776         "tt",
41777         "1868"
41778       ],
41779       [
41780         "Tunisia (‫تونس‬‎)",
41781         "tn",
41782         "216"
41783       ],
41784       [
41785         "Turkey (Türkiye)",
41786         "tr",
41787         "90"
41788       ],
41789       [
41790         "Turkmenistan",
41791         "tm",
41792         "993"
41793       ],
41794       [
41795         "Turks and Caicos Islands",
41796         "tc",
41797         "1649"
41798       ],
41799       [
41800         "Tuvalu",
41801         "tv",
41802         "688"
41803       ],
41804       [
41805         "U.S. Virgin Islands",
41806         "vi",
41807         "1340"
41808       ],
41809       [
41810         "Uganda",
41811         "ug",
41812         "256"
41813       ],
41814       [
41815         "Ukraine (Україна)",
41816         "ua",
41817         "380"
41818       ],
41819       [
41820         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
41821         "ae",
41822         "971"
41823       ],
41824       [
41825         "United Kingdom",
41826         "gb",
41827         "44",
41828         0
41829       ],
41830       [
41831         "United States",
41832         "us",
41833         "1",
41834         0
41835       ],
41836       [
41837         "Uruguay",
41838         "uy",
41839         "598"
41840       ],
41841       [
41842         "Uzbekistan (Oʻzbekiston)",
41843         "uz",
41844         "998"
41845       ],
41846       [
41847         "Vanuatu",
41848         "vu",
41849         "678"
41850       ],
41851       [
41852         "Vatican City (Città del Vaticano)",
41853         "va",
41854         "39",
41855         1
41856       ],
41857       [
41858         "Venezuela",
41859         "ve",
41860         "58"
41861       ],
41862       [
41863         "Vietnam (Việt Nam)",
41864         "vn",
41865         "84"
41866       ],
41867       [
41868         "Wallis and Futuna (Wallis-et-Futuna)",
41869         "wf",
41870         "681"
41871       ],
41872       [
41873         "Western Sahara (‫الصحراء الغربية‬‎)",
41874         "eh",
41875         "212",
41876         1
41877       ],
41878       [
41879         "Yemen (‫اليمن‬‎)",
41880         "ye",
41881         "967"
41882       ],
41883       [
41884         "Zambia",
41885         "zm",
41886         "260"
41887       ],
41888       [
41889         "Zimbabwe",
41890         "zw",
41891         "263"
41892       ],
41893       [
41894         "Åland Islands",
41895         "ax",
41896         "358",
41897         1
41898       ]
41899   ];
41900   
41901   return d;
41902 }/**
41903 *    This script refer to:
41904 *    Title: International Telephone Input
41905 *    Author: Jack O'Connor
41906 *    Code version:  v12.1.12
41907 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41908 **/
41909
41910 /**
41911  * @class Roo.bootstrap.PhoneInput
41912  * @extends Roo.bootstrap.TriggerField
41913  * An input with International dial-code selection
41914  
41915  * @cfg {String} defaultDialCode default '+852'
41916  * @cfg {Array} preferedCountries default []
41917   
41918  * @constructor
41919  * Create a new PhoneInput.
41920  * @param {Object} config Configuration options
41921  */
41922
41923 Roo.bootstrap.PhoneInput = function(config) {
41924     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
41925 };
41926
41927 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
41928         
41929         listWidth: undefined,
41930         
41931         selectedClass: 'active',
41932         
41933         invalidClass : "has-warning",
41934         
41935         validClass: 'has-success',
41936         
41937         allowed: '0123456789',
41938         
41939         max_length: 15,
41940         
41941         /**
41942          * @cfg {String} defaultDialCode The default dial code when initializing the input
41943          */
41944         defaultDialCode: '+852',
41945         
41946         /**
41947          * @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
41948          */
41949         preferedCountries: false,
41950         
41951         getAutoCreate : function()
41952         {
41953             var data = Roo.bootstrap.PhoneInputData();
41954             var align = this.labelAlign || this.parentLabelAlign();
41955             var id = Roo.id();
41956             
41957             this.allCountries = [];
41958             this.dialCodeMapping = [];
41959             
41960             for (var i = 0; i < data.length; i++) {
41961               var c = data[i];
41962               this.allCountries[i] = {
41963                 name: c[0],
41964                 iso2: c[1],
41965                 dialCode: c[2],
41966                 priority: c[3] || 0,
41967                 areaCodes: c[4] || null
41968               };
41969               this.dialCodeMapping[c[2]] = {
41970                   name: c[0],
41971                   iso2: c[1],
41972                   priority: c[3] || 0,
41973                   areaCodes: c[4] || null
41974               };
41975             }
41976             
41977             var cfg = {
41978                 cls: 'form-group',
41979                 cn: []
41980             };
41981             
41982             var input =  {
41983                 tag: 'input',
41984                 id : id,
41985                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
41986                 maxlength: this.max_length,
41987                 cls : 'form-control tel-input',
41988                 autocomplete: 'new-password'
41989             };
41990             
41991             var hiddenInput = {
41992                 tag: 'input',
41993                 type: 'hidden',
41994                 cls: 'hidden-tel-input'
41995             };
41996             
41997             if (this.name) {
41998                 hiddenInput.name = this.name;
41999             }
42000             
42001             if (this.disabled) {
42002                 input.disabled = true;
42003             }
42004             
42005             var flag_container = {
42006                 tag: 'div',
42007                 cls: 'flag-box',
42008                 cn: [
42009                     {
42010                         tag: 'div',
42011                         cls: 'flag'
42012                     },
42013                     {
42014                         tag: 'div',
42015                         cls: 'caret'
42016                     }
42017                 ]
42018             };
42019             
42020             var box = {
42021                 tag: 'div',
42022                 cls: this.hasFeedback ? 'has-feedback' : '',
42023                 cn: [
42024                     hiddenInput,
42025                     input,
42026                     {
42027                         tag: 'input',
42028                         cls: 'dial-code-holder',
42029                         disabled: true
42030                     }
42031                 ]
42032             };
42033             
42034             var container = {
42035                 cls: 'roo-select2-container input-group',
42036                 cn: [
42037                     flag_container,
42038                     box
42039                 ]
42040             };
42041             
42042             if (this.fieldLabel.length) {
42043                 var indicator = {
42044                     tag: 'i',
42045                     tooltip: 'This field is required'
42046                 };
42047                 
42048                 var label = {
42049                     tag: 'label',
42050                     'for':  id,
42051                     cls: 'control-label',
42052                     cn: []
42053                 };
42054                 
42055                 var label_text = {
42056                     tag: 'span',
42057                     html: this.fieldLabel
42058                 };
42059                 
42060                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42061                 label.cn = [
42062                     indicator,
42063                     label_text
42064                 ];
42065                 
42066                 if(this.indicatorpos == 'right') {
42067                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42068                     label.cn = [
42069                         label_text,
42070                         indicator
42071                     ];
42072                 }
42073                 
42074                 if(align == 'left') {
42075                     container = {
42076                         tag: 'div',
42077                         cn: [
42078                             container
42079                         ]
42080                     };
42081                     
42082                     if(this.labelWidth > 12){
42083                         label.style = "width: " + this.labelWidth + 'px';
42084                     }
42085                     if(this.labelWidth < 13 && this.labelmd == 0){
42086                         this.labelmd = this.labelWidth;
42087                     }
42088                     if(this.labellg > 0){
42089                         label.cls += ' col-lg-' + this.labellg;
42090                         input.cls += ' col-lg-' + (12 - this.labellg);
42091                     }
42092                     if(this.labelmd > 0){
42093                         label.cls += ' col-md-' + this.labelmd;
42094                         container.cls += ' col-md-' + (12 - this.labelmd);
42095                     }
42096                     if(this.labelsm > 0){
42097                         label.cls += ' col-sm-' + this.labelsm;
42098                         container.cls += ' col-sm-' + (12 - this.labelsm);
42099                     }
42100                     if(this.labelxs > 0){
42101                         label.cls += ' col-xs-' + this.labelxs;
42102                         container.cls += ' col-xs-' + (12 - this.labelxs);
42103                     }
42104                 }
42105             }
42106             
42107             cfg.cn = [
42108                 label,
42109                 container
42110             ];
42111             
42112             var settings = this;
42113             
42114             ['xs','sm','md','lg'].map(function(size){
42115                 if (settings[size]) {
42116                     cfg.cls += ' col-' + size + '-' + settings[size];
42117                 }
42118             });
42119             
42120             this.store = new Roo.data.Store({
42121                 proxy : new Roo.data.MemoryProxy({}),
42122                 reader : new Roo.data.JsonReader({
42123                     fields : [
42124                         {
42125                             'name' : 'name',
42126                             'type' : 'string'
42127                         },
42128                         {
42129                             'name' : 'iso2',
42130                             'type' : 'string'
42131                         },
42132                         {
42133                             'name' : 'dialCode',
42134                             'type' : 'string'
42135                         },
42136                         {
42137                             'name' : 'priority',
42138                             'type' : 'string'
42139                         },
42140                         {
42141                             'name' : 'areaCodes',
42142                             'type' : 'string'
42143                         }
42144                     ]
42145                 })
42146             });
42147             
42148             if(!this.preferedCountries) {
42149                 this.preferedCountries = [
42150                     'hk',
42151                     'gb',
42152                     'us'
42153                 ];
42154             }
42155             
42156             var p = this.preferedCountries.reverse();
42157             
42158             if(p) {
42159                 for (var i = 0; i < p.length; i++) {
42160                     for (var j = 0; j < this.allCountries.length; j++) {
42161                         if(this.allCountries[j].iso2 == p[i]) {
42162                             var t = this.allCountries[j];
42163                             this.allCountries.splice(j,1);
42164                             this.allCountries.unshift(t);
42165                         }
42166                     } 
42167                 }
42168             }
42169             
42170             this.store.proxy.data = {
42171                 success: true,
42172                 data: this.allCountries
42173             };
42174             
42175             return cfg;
42176         },
42177         
42178         initEvents : function()
42179         {
42180             this.createList();
42181             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42182             
42183             this.indicator = this.indicatorEl();
42184             this.flag = this.flagEl();
42185             this.dialCodeHolder = this.dialCodeHolderEl();
42186             
42187             this.trigger = this.el.select('div.flag-box',true).first();
42188             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42189             
42190             var _this = this;
42191             
42192             (function(){
42193                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42194                 _this.list.setWidth(lw);
42195             }).defer(100);
42196             
42197             this.list.on('mouseover', this.onViewOver, this);
42198             this.list.on('mousemove', this.onViewMove, this);
42199             this.inputEl().on("keyup", this.onKeyUp, this);
42200             this.inputEl().on("keypress", this.onKeyPress, this);
42201             
42202             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42203
42204             this.view = new Roo.View(this.list, this.tpl, {
42205                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42206             });
42207             
42208             this.view.on('click', this.onViewClick, this);
42209             this.setValue(this.defaultDialCode);
42210         },
42211         
42212         onTriggerClick : function(e)
42213         {
42214             Roo.log('trigger click');
42215             if(this.disabled){
42216                 return;
42217             }
42218             
42219             if(this.isExpanded()){
42220                 this.collapse();
42221                 this.hasFocus = false;
42222             }else {
42223                 this.store.load({});
42224                 this.hasFocus = true;
42225                 this.expand();
42226             }
42227         },
42228         
42229         isExpanded : function()
42230         {
42231             return this.list.isVisible();
42232         },
42233         
42234         collapse : function()
42235         {
42236             if(!this.isExpanded()){
42237                 return;
42238             }
42239             this.list.hide();
42240             Roo.get(document).un('mousedown', this.collapseIf, this);
42241             Roo.get(document).un('mousewheel', this.collapseIf, this);
42242             this.fireEvent('collapse', this);
42243             this.validate();
42244         },
42245         
42246         expand : function()
42247         {
42248             Roo.log('expand');
42249
42250             if(this.isExpanded() || !this.hasFocus){
42251                 return;
42252             }
42253             
42254             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42255             this.list.setWidth(lw);
42256             
42257             this.list.show();
42258             this.restrictHeight();
42259             
42260             Roo.get(document).on('mousedown', this.collapseIf, this);
42261             Roo.get(document).on('mousewheel', this.collapseIf, this);
42262             
42263             this.fireEvent('expand', this);
42264         },
42265         
42266         restrictHeight : function()
42267         {
42268             this.list.alignTo(this.inputEl(), this.listAlign);
42269             this.list.alignTo(this.inputEl(), this.listAlign);
42270         },
42271         
42272         onViewOver : function(e, t)
42273         {
42274             if(this.inKeyMode){
42275                 return;
42276             }
42277             var item = this.view.findItemFromChild(t);
42278             
42279             if(item){
42280                 var index = this.view.indexOf(item);
42281                 this.select(index, false);
42282             }
42283         },
42284
42285         // private
42286         onViewClick : function(view, doFocus, el, e)
42287         {
42288             var index = this.view.getSelectedIndexes()[0];
42289             
42290             var r = this.store.getAt(index);
42291             
42292             if(r){
42293                 this.onSelect(r, index);
42294             }
42295             if(doFocus !== false && !this.blockFocus){
42296                 this.inputEl().focus();
42297             }
42298         },
42299         
42300         onViewMove : function(e, t)
42301         {
42302             this.inKeyMode = false;
42303         },
42304         
42305         select : function(index, scrollIntoView)
42306         {
42307             this.selectedIndex = index;
42308             this.view.select(index);
42309             if(scrollIntoView !== false){
42310                 var el = this.view.getNode(index);
42311                 if(el){
42312                     this.list.scrollChildIntoView(el, false);
42313                 }
42314             }
42315         },
42316         
42317         createList : function()
42318         {
42319             this.list = Roo.get(document.body).createChild({
42320                 tag: 'ul',
42321                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42322                 style: 'display:none'
42323             });
42324             
42325             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42326         },
42327         
42328         collapseIf : function(e)
42329         {
42330             var in_combo  = e.within(this.el);
42331             var in_list =  e.within(this.list);
42332             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42333             
42334             if (in_combo || in_list || is_list) {
42335                 return;
42336             }
42337             this.collapse();
42338         },
42339         
42340         onSelect : function(record, index)
42341         {
42342             if(this.fireEvent('beforeselect', this, record, index) !== false){
42343                 
42344                 this.setFlagClass(record.data.iso2);
42345                 this.setDialCode(record.data.dialCode);
42346                 this.hasFocus = false;
42347                 this.collapse();
42348                 this.fireEvent('select', this, record, index);
42349             }
42350         },
42351         
42352         flagEl : function()
42353         {
42354             var flag = this.el.select('div.flag',true).first();
42355             if(!flag){
42356                 return false;
42357             }
42358             return flag;
42359         },
42360         
42361         dialCodeHolderEl : function()
42362         {
42363             var d = this.el.select('input.dial-code-holder',true).first();
42364             if(!d){
42365                 return false;
42366             }
42367             return d;
42368         },
42369         
42370         setDialCode : function(v)
42371         {
42372             this.dialCodeHolder.dom.value = '+'+v;
42373         },
42374         
42375         setFlagClass : function(n)
42376         {
42377             this.flag.dom.className = 'flag '+n;
42378         },
42379         
42380         getValue : function()
42381         {
42382             var v = this.inputEl().getValue();
42383             if(this.dialCodeHolder) {
42384                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42385             }
42386             return v;
42387         },
42388         
42389         setValue : function(v)
42390         {
42391             var d = this.getDialCode(v);
42392             
42393             //invalid dial code
42394             if(v.length == 0 || !d || d.length == 0) {
42395                 if(this.rendered){
42396                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42397                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42398                 }
42399                 return;
42400             }
42401             
42402             //valid dial code
42403             this.setFlagClass(this.dialCodeMapping[d].iso2);
42404             this.setDialCode(d);
42405             this.inputEl().dom.value = v.replace('+'+d,'');
42406             this.hiddenEl().dom.value = this.getValue();
42407             
42408             this.validate();
42409         },
42410         
42411         getDialCode : function(v)
42412         {
42413             v = v ||  '';
42414             
42415             if (v.length == 0) {
42416                 return this.dialCodeHolder.dom.value;
42417             }
42418             
42419             var dialCode = "";
42420             if (v.charAt(0) != "+") {
42421                 return false;
42422             }
42423             var numericChars = "";
42424             for (var i = 1; i < v.length; i++) {
42425               var c = v.charAt(i);
42426               if (!isNaN(c)) {
42427                 numericChars += c;
42428                 if (this.dialCodeMapping[numericChars]) {
42429                   dialCode = v.substr(1, i);
42430                 }
42431                 if (numericChars.length == 4) {
42432                   break;
42433                 }
42434               }
42435             }
42436             return dialCode;
42437         },
42438         
42439         reset : function()
42440         {
42441             this.setValue(this.defaultDialCode);
42442             this.validate();
42443         },
42444         
42445         hiddenEl : function()
42446         {
42447             return this.el.select('input.hidden-tel-input',true).first();
42448         },
42449         
42450         // after setting val
42451         onKeyUp : function(e){
42452             this.setValue(this.getValue());
42453         },
42454         
42455         onKeyPress : function(e){
42456             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42457                 e.stopEvent();
42458             }
42459         }
42460         
42461 });
42462 /**
42463  * @class Roo.bootstrap.MoneyField
42464  * @extends Roo.bootstrap.ComboBox
42465  * Bootstrap MoneyField class
42466  * 
42467  * @constructor
42468  * Create a new MoneyField.
42469  * @param {Object} config Configuration options
42470  */
42471
42472 Roo.bootstrap.MoneyField = function(config) {
42473     
42474     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42475     
42476 };
42477
42478 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42479     
42480     /**
42481      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42482      */
42483     allowDecimals : true,
42484     /**
42485      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42486      */
42487     decimalSeparator : ".",
42488     /**
42489      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42490      */
42491     decimalPrecision : 0,
42492     /**
42493      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42494      */
42495     allowNegative : true,
42496     /**
42497      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42498      */
42499     allowZero: true,
42500     /**
42501      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42502      */
42503     minValue : Number.NEGATIVE_INFINITY,
42504     /**
42505      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42506      */
42507     maxValue : Number.MAX_VALUE,
42508     /**
42509      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42510      */
42511     minText : "The minimum value for this field is {0}",
42512     /**
42513      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42514      */
42515     maxText : "The maximum value for this field is {0}",
42516     /**
42517      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
42518      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42519      */
42520     nanText : "{0} is not a valid number",
42521     /**
42522      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42523      */
42524     castInt : true,
42525     /**
42526      * @cfg {String} defaults currency of the MoneyField
42527      * value should be in lkey
42528      */
42529     defaultCurrency : false,
42530     /**
42531      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42532      */
42533     thousandsDelimiter : false,
42534     /**
42535      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42536      */
42537     max_length: false,
42538     
42539     inputlg : 9,
42540     inputmd : 9,
42541     inputsm : 9,
42542     inputxs : 6,
42543     
42544     store : false,
42545     
42546     getAutoCreate : function()
42547     {
42548         var align = this.labelAlign || this.parentLabelAlign();
42549         
42550         var id = Roo.id();
42551
42552         var cfg = {
42553             cls: 'form-group',
42554             cn: []
42555         };
42556
42557         var input =  {
42558             tag: 'input',
42559             id : id,
42560             cls : 'form-control roo-money-amount-input',
42561             autocomplete: 'new-password'
42562         };
42563         
42564         var hiddenInput = {
42565             tag: 'input',
42566             type: 'hidden',
42567             id: Roo.id(),
42568             cls: 'hidden-number-input'
42569         };
42570         
42571         if(this.max_length) {
42572             input.maxlength = this.max_length; 
42573         }
42574         
42575         if (this.name) {
42576             hiddenInput.name = this.name;
42577         }
42578
42579         if (this.disabled) {
42580             input.disabled = true;
42581         }
42582
42583         var clg = 12 - this.inputlg;
42584         var cmd = 12 - this.inputmd;
42585         var csm = 12 - this.inputsm;
42586         var cxs = 12 - this.inputxs;
42587         
42588         var container = {
42589             tag : 'div',
42590             cls : 'row roo-money-field',
42591             cn : [
42592                 {
42593                     tag : 'div',
42594                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42595                     cn : [
42596                         {
42597                             tag : 'div',
42598                             cls: 'roo-select2-container input-group',
42599                             cn: [
42600                                 {
42601                                     tag : 'input',
42602                                     cls : 'form-control roo-money-currency-input',
42603                                     autocomplete: 'new-password',
42604                                     readOnly : 1,
42605                                     name : this.currencyName
42606                                 },
42607                                 {
42608                                     tag :'span',
42609                                     cls : 'input-group-addon',
42610                                     cn : [
42611                                         {
42612                                             tag: 'span',
42613                                             cls: 'caret'
42614                                         }
42615                                     ]
42616                                 }
42617                             ]
42618                         }
42619                     ]
42620                 },
42621                 {
42622                     tag : 'div',
42623                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42624                     cn : [
42625                         {
42626                             tag: 'div',
42627                             cls: this.hasFeedback ? 'has-feedback' : '',
42628                             cn: [
42629                                 input
42630                             ]
42631                         }
42632                     ]
42633                 }
42634             ]
42635             
42636         };
42637         
42638         if (this.fieldLabel.length) {
42639             var indicator = {
42640                 tag: 'i',
42641                 tooltip: 'This field is required'
42642             };
42643
42644             var label = {
42645                 tag: 'label',
42646                 'for':  id,
42647                 cls: 'control-label',
42648                 cn: []
42649             };
42650
42651             var label_text = {
42652                 tag: 'span',
42653                 html: this.fieldLabel
42654             };
42655
42656             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42657             label.cn = [
42658                 indicator,
42659                 label_text
42660             ];
42661
42662             if(this.indicatorpos == 'right') {
42663                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42664                 label.cn = [
42665                     label_text,
42666                     indicator
42667                 ];
42668             }
42669
42670             if(align == 'left') {
42671                 container = {
42672                     tag: 'div',
42673                     cn: [
42674                         container
42675                     ]
42676                 };
42677
42678                 if(this.labelWidth > 12){
42679                     label.style = "width: " + this.labelWidth + 'px';
42680                 }
42681                 if(this.labelWidth < 13 && this.labelmd == 0){
42682                     this.labelmd = this.labelWidth;
42683                 }
42684                 if(this.labellg > 0){
42685                     label.cls += ' col-lg-' + this.labellg;
42686                     input.cls += ' col-lg-' + (12 - this.labellg);
42687                 }
42688                 if(this.labelmd > 0){
42689                     label.cls += ' col-md-' + this.labelmd;
42690                     container.cls += ' col-md-' + (12 - this.labelmd);
42691                 }
42692                 if(this.labelsm > 0){
42693                     label.cls += ' col-sm-' + this.labelsm;
42694                     container.cls += ' col-sm-' + (12 - this.labelsm);
42695                 }
42696                 if(this.labelxs > 0){
42697                     label.cls += ' col-xs-' + this.labelxs;
42698                     container.cls += ' col-xs-' + (12 - this.labelxs);
42699                 }
42700             }
42701         }
42702
42703         cfg.cn = [
42704             label,
42705             container,
42706             hiddenInput
42707         ];
42708         
42709         var settings = this;
42710
42711         ['xs','sm','md','lg'].map(function(size){
42712             if (settings[size]) {
42713                 cfg.cls += ' col-' + size + '-' + settings[size];
42714             }
42715         });
42716         
42717         return cfg;
42718     },
42719     
42720     initEvents : function()
42721     {
42722         this.indicator = this.indicatorEl();
42723         
42724         this.initCurrencyEvent();
42725         
42726         this.initNumberEvent();
42727     },
42728     
42729     initCurrencyEvent : function()
42730     {
42731         if (!this.store) {
42732             throw "can not find store for combo";
42733         }
42734         
42735         this.store = Roo.factory(this.store, Roo.data);
42736         this.store.parent = this;
42737         
42738         this.createList();
42739         
42740         this.triggerEl = this.el.select('.input-group-addon', true).first();
42741         
42742         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42743         
42744         var _this = this;
42745         
42746         (function(){
42747             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42748             _this.list.setWidth(lw);
42749         }).defer(100);
42750         
42751         this.list.on('mouseover', this.onViewOver, this);
42752         this.list.on('mousemove', this.onViewMove, this);
42753         this.list.on('scroll', this.onViewScroll, this);
42754         
42755         if(!this.tpl){
42756             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42757         }
42758         
42759         this.view = new Roo.View(this.list, this.tpl, {
42760             singleSelect:true, store: this.store, selectedClass: this.selectedClass
42761         });
42762         
42763         this.view.on('click', this.onViewClick, this);
42764         
42765         this.store.on('beforeload', this.onBeforeLoad, this);
42766         this.store.on('load', this.onLoad, this);
42767         this.store.on('loadexception', this.onLoadException, this);
42768         
42769         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
42770             "up" : function(e){
42771                 this.inKeyMode = true;
42772                 this.selectPrev();
42773             },
42774
42775             "down" : function(e){
42776                 if(!this.isExpanded()){
42777                     this.onTriggerClick();
42778                 }else{
42779                     this.inKeyMode = true;
42780                     this.selectNext();
42781                 }
42782             },
42783
42784             "enter" : function(e){
42785                 this.collapse();
42786                 
42787                 if(this.fireEvent("specialkey", this, e)){
42788                     this.onViewClick(false);
42789                 }
42790                 
42791                 return true;
42792             },
42793
42794             "esc" : function(e){
42795                 this.collapse();
42796             },
42797
42798             "tab" : function(e){
42799                 this.collapse();
42800                 
42801                 if(this.fireEvent("specialkey", this, e)){
42802                     this.onViewClick(false);
42803                 }
42804                 
42805                 return true;
42806             },
42807
42808             scope : this,
42809
42810             doRelay : function(foo, bar, hname){
42811                 if(hname == 'down' || this.scope.isExpanded()){
42812                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42813                 }
42814                 return true;
42815             },
42816
42817             forceKeyDown: true
42818         });
42819         
42820         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
42821         
42822     },
42823     
42824     initNumberEvent : function(e)
42825     {
42826         this.inputEl().on("keydown" , this.fireKey,  this);
42827         this.inputEl().on("focus", this.onFocus,  this);
42828         this.inputEl().on("blur", this.onBlur,  this);
42829         
42830         this.inputEl().relayEvent('keyup', this);
42831         
42832         if(this.indicator){
42833             this.indicator.addClass('invisible');
42834         }
42835  
42836         this.originalValue = this.getValue();
42837         
42838         if(this.validationEvent == 'keyup'){
42839             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
42840             this.inputEl().on('keyup', this.filterValidation, this);
42841         }
42842         else if(this.validationEvent !== false){
42843             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
42844         }
42845         
42846         if(this.selectOnFocus){
42847             this.on("focus", this.preFocus, this);
42848             
42849         }
42850         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
42851             this.inputEl().on("keypress", this.filterKeys, this);
42852         } else {
42853             this.inputEl().relayEvent('keypress', this);
42854         }
42855         
42856         var allowed = "0123456789";
42857         
42858         if(this.allowDecimals){
42859             allowed += this.decimalSeparator;
42860         }
42861         
42862         if(this.allowNegative){
42863             allowed += "-";
42864         }
42865         
42866         if(this.thousandsDelimiter) {
42867             allowed += ",";
42868         }
42869         
42870         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
42871         
42872         var keyPress = function(e){
42873             
42874             var k = e.getKey();
42875             
42876             var c = e.getCharCode();
42877             
42878             if(
42879                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
42880                     allowed.indexOf(String.fromCharCode(c)) === -1
42881             ){
42882                 e.stopEvent();
42883                 return;
42884             }
42885             
42886             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
42887                 return;
42888             }
42889             
42890             if(allowed.indexOf(String.fromCharCode(c)) === -1){
42891                 e.stopEvent();
42892             }
42893         };
42894         
42895         this.inputEl().on("keypress", keyPress, this);
42896         
42897     },
42898     
42899     onTriggerClick : function(e)
42900     {   
42901         if(this.disabled){
42902             return;
42903         }
42904         
42905         this.page = 0;
42906         this.loadNext = false;
42907         
42908         if(this.isExpanded()){
42909             this.collapse();
42910             return;
42911         }
42912         
42913         this.hasFocus = true;
42914         
42915         if(this.triggerAction == 'all') {
42916             this.doQuery(this.allQuery, true);
42917             return;
42918         }
42919         
42920         this.doQuery(this.getRawValue());
42921     },
42922     
42923     getCurrency : function()
42924     {   
42925         var v = this.currencyEl().getValue();
42926         
42927         return v;
42928     },
42929     
42930     restrictHeight : function()
42931     {
42932         this.list.alignTo(this.currencyEl(), this.listAlign);
42933         this.list.alignTo(this.currencyEl(), this.listAlign);
42934     },
42935     
42936     onViewClick : function(view, doFocus, el, e)
42937     {
42938         var index = this.view.getSelectedIndexes()[0];
42939         
42940         var r = this.store.getAt(index);
42941         
42942         if(r){
42943             this.onSelect(r, index);
42944         }
42945     },
42946     
42947     onSelect : function(record, index){
42948         
42949         if(this.fireEvent('beforeselect', this, record, index) !== false){
42950         
42951             this.setFromCurrencyData(index > -1 ? record.data : false);
42952             
42953             this.collapse();
42954             
42955             this.fireEvent('select', this, record, index);
42956         }
42957     },
42958     
42959     setFromCurrencyData : function(o)
42960     {
42961         var currency = '';
42962         
42963         this.lastCurrency = o;
42964         
42965         if (this.currencyField) {
42966             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
42967         } else {
42968             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
42969         }
42970         
42971         this.lastSelectionText = currency;
42972         
42973         //setting default currency
42974         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
42975             this.setCurrency(this.defaultCurrency);
42976             return;
42977         }
42978         
42979         this.setCurrency(currency);
42980     },
42981     
42982     setFromData : function(o)
42983     {
42984         var c = {};
42985         
42986         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
42987         
42988         this.setFromCurrencyData(c);
42989         
42990         var value = '';
42991         
42992         if (this.name) {
42993             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
42994         } else {
42995             Roo.log('no value set for '+ (this.name ? this.name : this.id));
42996         }
42997         
42998         this.setValue(value);
42999         
43000     },
43001     
43002     setCurrency : function(v)
43003     {   
43004         this.currencyValue = v;
43005         
43006         if(this.rendered){
43007             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43008             this.validate();
43009         }
43010     },
43011     
43012     setValue : function(v)
43013     {
43014         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43015         
43016         this.value = v;
43017         
43018         if(this.rendered){
43019             
43020             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43021             
43022             this.inputEl().dom.value = (v == '') ? '' :
43023                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43024             
43025             if(!this.allowZero && v === '0') {
43026                 this.hiddenEl().dom.value = '';
43027                 this.inputEl().dom.value = '';
43028             }
43029             
43030             this.validate();
43031         }
43032     },
43033     
43034     getRawValue : function()
43035     {
43036         var v = this.inputEl().getValue();
43037         
43038         return v;
43039     },
43040     
43041     getValue : function()
43042     {
43043         return this.fixPrecision(this.parseValue(this.getRawValue()));
43044     },
43045     
43046     parseValue : function(value)
43047     {
43048         if(this.thousandsDelimiter) {
43049             value += "";
43050             r = new RegExp(",", "g");
43051             value = value.replace(r, "");
43052         }
43053         
43054         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43055         return isNaN(value) ? '' : value;
43056         
43057     },
43058     
43059     fixPrecision : function(value)
43060     {
43061         if(this.thousandsDelimiter) {
43062             value += "";
43063             r = new RegExp(",", "g");
43064             value = value.replace(r, "");
43065         }
43066         
43067         var nan = isNaN(value);
43068         
43069         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43070             return nan ? '' : value;
43071         }
43072         return parseFloat(value).toFixed(this.decimalPrecision);
43073     },
43074     
43075     decimalPrecisionFcn : function(v)
43076     {
43077         return Math.floor(v);
43078     },
43079     
43080     validateValue : function(value)
43081     {
43082         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43083             return false;
43084         }
43085         
43086         var num = this.parseValue(value);
43087         
43088         if(isNaN(num)){
43089             this.markInvalid(String.format(this.nanText, value));
43090             return false;
43091         }
43092         
43093         if(num < this.minValue){
43094             this.markInvalid(String.format(this.minText, this.minValue));
43095             return false;
43096         }
43097         
43098         if(num > this.maxValue){
43099             this.markInvalid(String.format(this.maxText, this.maxValue));
43100             return false;
43101         }
43102         
43103         return true;
43104     },
43105     
43106     validate : function()
43107     {
43108         if(this.disabled || this.allowBlank){
43109             this.markValid();
43110             return true;
43111         }
43112         
43113         var currency = this.getCurrency();
43114         
43115         if(this.validateValue(this.getRawValue()) && currency.length){
43116             this.markValid();
43117             return true;
43118         }
43119         
43120         this.markInvalid();
43121         return false;
43122     },
43123     
43124     getName: function()
43125     {
43126         return this.name;
43127     },
43128     
43129     beforeBlur : function()
43130     {
43131         if(!this.castInt){
43132             return;
43133         }
43134         
43135         var v = this.parseValue(this.getRawValue());
43136         
43137         if(v || v == 0){
43138             this.setValue(v);
43139         }
43140     },
43141     
43142     onBlur : function()
43143     {
43144         this.beforeBlur();
43145         
43146         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43147             //this.el.removeClass(this.focusClass);
43148         }
43149         
43150         this.hasFocus = false;
43151         
43152         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43153             this.validate();
43154         }
43155         
43156         var v = this.getValue();
43157         
43158         if(String(v) !== String(this.startValue)){
43159             this.fireEvent('change', this, v, this.startValue);
43160         }
43161         
43162         this.fireEvent("blur", this);
43163     },
43164     
43165     inputEl : function()
43166     {
43167         return this.el.select('.roo-money-amount-input', true).first();
43168     },
43169     
43170     currencyEl : function()
43171     {
43172         return this.el.select('.roo-money-currency-input', true).first();
43173     },
43174     
43175     hiddenEl : function()
43176     {
43177         return this.el.select('input.hidden-number-input',true).first();
43178     }
43179     
43180 });/**
43181  * @class Roo.bootstrap.BezierSignature
43182  * @extends Roo.bootstrap.Component
43183  * Bootstrap BezierSignature class
43184  * This script refer to:
43185  *    Title: Signature Pad
43186  *    Author: szimek
43187  *    Availability: https://github.com/szimek/signature_pad
43188  *
43189  * @constructor
43190  * Create a new BezierSignature
43191  * @param {Object} config The config object
43192  */
43193
43194 Roo.bootstrap.BezierSignature = function(config){
43195     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43196     this.addEvents({
43197         "resize" : true
43198     });
43199 };
43200
43201 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43202 {
43203      
43204     curve_data: [],
43205     
43206     is_empty: true,
43207     
43208     mouse_btn_down: true,
43209     
43210     /**
43211      * @cfg {int} canvas height
43212      */
43213     canvas_height: '200px',
43214     
43215     /**
43216      * @cfg {float|function} Radius of a single dot.
43217      */ 
43218     dot_size: false,
43219     
43220     /**
43221      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43222      */
43223     min_width: 0.5,
43224     
43225     /**
43226      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43227      */
43228     max_width: 2.5,
43229     
43230     /**
43231      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43232      */
43233     throttle: 16,
43234     
43235     /**
43236      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43237      */
43238     min_distance: 5,
43239     
43240     /**
43241      * @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.
43242      */
43243     bg_color: 'rgba(0, 0, 0, 0)',
43244     
43245     /**
43246      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43247      */
43248     dot_color: 'black',
43249     
43250     /**
43251      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43252      */ 
43253     velocity_filter_weight: 0.7,
43254     
43255     /**
43256      * @cfg {function} Callback when stroke begin. 
43257      */
43258     onBegin: false,
43259     
43260     /**
43261      * @cfg {function} Callback when stroke end.
43262      */
43263     onEnd: false,
43264     
43265     getAutoCreate : function()
43266     {
43267         var cls = 'roo-signature column';
43268         
43269         if(this.cls){
43270             cls += ' ' + this.cls;
43271         }
43272         
43273         var col_sizes = [
43274             'lg',
43275             'md',
43276             'sm',
43277             'xs'
43278         ];
43279         
43280         for(var i = 0; i < col_sizes.length; i++) {
43281             if(this[col_sizes[i]]) {
43282                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43283             }
43284         }
43285         
43286         var cfg = {
43287             tag: 'div',
43288             cls: cls,
43289             cn: [
43290                 {
43291                     tag: 'div',
43292                     cls: 'roo-signature-body',
43293                     cn: [
43294                         {
43295                             tag: 'canvas',
43296                             cls: 'roo-signature-body-canvas',
43297                             height: this.canvas_height,
43298                             width: this.canvas_width
43299                         }
43300                     ]
43301                 },
43302                 {
43303                     tag: 'input',
43304                     type: 'file',
43305                     style: 'display: none'
43306                 }
43307             ]
43308         };
43309         
43310         return cfg;
43311     },
43312     
43313     initEvents: function() 
43314     {
43315         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43316         
43317         var canvas = this.canvasEl();
43318         
43319         // mouse && touch event swapping...
43320         canvas.dom.style.touchAction = 'none';
43321         canvas.dom.style.msTouchAction = 'none';
43322         
43323         this.mouse_btn_down = false;
43324         canvas.on('mousedown', this._handleMouseDown, this);
43325         canvas.on('mousemove', this._handleMouseMove, this);
43326         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43327         
43328         if (window.PointerEvent) {
43329             canvas.on('pointerdown', this._handleMouseDown, this);
43330             canvas.on('pointermove', this._handleMouseMove, this);
43331             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43332         }
43333         
43334         if ('ontouchstart' in window) {
43335             canvas.on('touchstart', this._handleTouchStart, this);
43336             canvas.on('touchmove', this._handleTouchMove, this);
43337             canvas.on('touchend', this._handleTouchEnd, this);
43338         }
43339         
43340         Roo.EventManager.onWindowResize(this.resize, this, true);
43341         
43342         // file input event
43343         this.fileEl().on('change', this.uploadImage, this);
43344         
43345         this.clear();
43346         
43347         this.resize();
43348     },
43349     
43350     resize: function(){
43351         
43352         var canvas = this.canvasEl().dom;
43353         var ctx = this.canvasElCtx();
43354         var img_data = false;
43355         
43356         if(canvas.width > 0) {
43357             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43358         }
43359         // setting canvas width will clean img data
43360         canvas.width = 0;
43361         
43362         var style = window.getComputedStyle ? 
43363             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43364             
43365         var padding_left = parseInt(style.paddingLeft) || 0;
43366         var padding_right = parseInt(style.paddingRight) || 0;
43367         
43368         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43369         
43370         if(img_data) {
43371             ctx.putImageData(img_data, 0, 0);
43372         }
43373     },
43374     
43375     _handleMouseDown: function(e)
43376     {
43377         if (e.browserEvent.which === 1) {
43378             this.mouse_btn_down = true;
43379             this.strokeBegin(e);
43380         }
43381     },
43382     
43383     _handleMouseMove: function (e)
43384     {
43385         if (this.mouse_btn_down) {
43386             this.strokeMoveUpdate(e);
43387         }
43388     },
43389     
43390     _handleMouseUp: function (e)
43391     {
43392         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43393             this.mouse_btn_down = false;
43394             this.strokeEnd(e);
43395         }
43396     },
43397     
43398     _handleTouchStart: function (e) {
43399         
43400         e.preventDefault();
43401         if (e.browserEvent.targetTouches.length === 1) {
43402             // var touch = e.browserEvent.changedTouches[0];
43403             // this.strokeBegin(touch);
43404             
43405              this.strokeBegin(e); // assume e catching the correct xy...
43406         }
43407     },
43408     
43409     _handleTouchMove: function (e) {
43410         e.preventDefault();
43411         // var touch = event.targetTouches[0];
43412         // _this._strokeMoveUpdate(touch);
43413         this.strokeMoveUpdate(e);
43414     },
43415     
43416     _handleTouchEnd: function (e) {
43417         var wasCanvasTouched = e.target === this.canvasEl().dom;
43418         if (wasCanvasTouched) {
43419             e.preventDefault();
43420             // var touch = event.changedTouches[0];
43421             // _this._strokeEnd(touch);
43422             this.strokeEnd(e);
43423         }
43424     },
43425     
43426     reset: function () {
43427         this._lastPoints = [];
43428         this._lastVelocity = 0;
43429         this._lastWidth = (this.min_width + this.max_width) / 2;
43430         this.canvasElCtx().fillStyle = this.dot_color;
43431     },
43432     
43433     strokeMoveUpdate: function(e)
43434     {
43435         this.strokeUpdate(e);
43436         
43437         if (this.throttle) {
43438             this.throttleStroke(this.strokeUpdate, this.throttle);
43439         }
43440         else {
43441             this.strokeUpdate(e);
43442         }
43443     },
43444     
43445     strokeBegin: function(e)
43446     {
43447         var newPointGroup = {
43448             color: this.dot_color,
43449             points: []
43450         };
43451         
43452         if (typeof this.onBegin === 'function') {
43453             this.onBegin(e);
43454         }
43455         
43456         this.curve_data.push(newPointGroup);
43457         this.reset();
43458         this.strokeUpdate(e);
43459     },
43460     
43461     strokeUpdate: function(e)
43462     {
43463         var rect = this.canvasEl().dom.getBoundingClientRect();
43464         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43465         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43466         var lastPoints = lastPointGroup.points;
43467         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43468         var isLastPointTooClose = lastPoint
43469             ? point.distanceTo(lastPoint) <= this.min_distance
43470             : false;
43471         var color = lastPointGroup.color;
43472         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43473             var curve = this.addPoint(point);
43474             if (!lastPoint) {
43475                 this.drawDot({color: color, point: point});
43476             }
43477             else if (curve) {
43478                 this.drawCurve({color: color, curve: curve});
43479             }
43480             lastPoints.push({
43481                 time: point.time,
43482                 x: point.x,
43483                 y: point.y
43484             });
43485         }
43486     },
43487     
43488     strokeEnd: function(e)
43489     {
43490         this.strokeUpdate(e);
43491         if (typeof this.onEnd === 'function') {
43492             this.onEnd(e);
43493         }
43494     },
43495     
43496     addPoint:  function (point) {
43497         var _lastPoints = this._lastPoints;
43498         _lastPoints.push(point);
43499         if (_lastPoints.length > 2) {
43500             if (_lastPoints.length === 3) {
43501                 _lastPoints.unshift(_lastPoints[0]);
43502             }
43503             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43504             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43505             _lastPoints.shift();
43506             return curve;
43507         }
43508         return null;
43509     },
43510     
43511     calculateCurveWidths: function (startPoint, endPoint) {
43512         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43513             (1 - this.velocity_filter_weight) * this._lastVelocity;
43514
43515         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43516         var widths = {
43517             end: newWidth,
43518             start: this._lastWidth
43519         };
43520         
43521         this._lastVelocity = velocity;
43522         this._lastWidth = newWidth;
43523         return widths;
43524     },
43525     
43526     drawDot: function (_a) {
43527         var color = _a.color, point = _a.point;
43528         var ctx = this.canvasElCtx();
43529         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43530         ctx.beginPath();
43531         this.drawCurveSegment(point.x, point.y, width);
43532         ctx.closePath();
43533         ctx.fillStyle = color;
43534         ctx.fill();
43535     },
43536     
43537     drawCurve: function (_a) {
43538         var color = _a.color, curve = _a.curve;
43539         var ctx = this.canvasElCtx();
43540         var widthDelta = curve.endWidth - curve.startWidth;
43541         var drawSteps = Math.floor(curve.length()) * 2;
43542         ctx.beginPath();
43543         ctx.fillStyle = color;
43544         for (var i = 0; i < drawSteps; i += 1) {
43545         var t = i / drawSteps;
43546         var tt = t * t;
43547         var ttt = tt * t;
43548         var u = 1 - t;
43549         var uu = u * u;
43550         var uuu = uu * u;
43551         var x = uuu * curve.startPoint.x;
43552         x += 3 * uu * t * curve.control1.x;
43553         x += 3 * u * tt * curve.control2.x;
43554         x += ttt * curve.endPoint.x;
43555         var y = uuu * curve.startPoint.y;
43556         y += 3 * uu * t * curve.control1.y;
43557         y += 3 * u * tt * curve.control2.y;
43558         y += ttt * curve.endPoint.y;
43559         var width = curve.startWidth + ttt * widthDelta;
43560         this.drawCurveSegment(x, y, width);
43561         }
43562         ctx.closePath();
43563         ctx.fill();
43564     },
43565     
43566     drawCurveSegment: function (x, y, width) {
43567         var ctx = this.canvasElCtx();
43568         ctx.moveTo(x, y);
43569         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43570         this.is_empty = false;
43571     },
43572     
43573     clear: function()
43574     {
43575         var ctx = this.canvasElCtx();
43576         var canvas = this.canvasEl().dom;
43577         ctx.fillStyle = this.bg_color;
43578         ctx.clearRect(0, 0, canvas.width, canvas.height);
43579         ctx.fillRect(0, 0, canvas.width, canvas.height);
43580         this.curve_data = [];
43581         this.reset();
43582         this.is_empty = true;
43583     },
43584     
43585     fileEl: function()
43586     {
43587         return  this.el.select('input',true).first();
43588     },
43589     
43590     canvasEl: function()
43591     {
43592         return this.el.select('canvas',true).first();
43593     },
43594     
43595     canvasElCtx: function()
43596     {
43597         return this.el.select('canvas',true).first().dom.getContext('2d');
43598     },
43599     
43600     getImage: function(type)
43601     {
43602         if(this.is_empty) {
43603             return false;
43604         }
43605         
43606         // encryption ?
43607         return this.canvasEl().dom.toDataURL('image/'+type, 1);
43608     },
43609     
43610     drawFromImage: function(img_src)
43611     {
43612         var img = new Image();
43613         
43614         img.onload = function(){
43615             this.canvasElCtx().drawImage(img, 0, 0);
43616         }.bind(this);
43617         
43618         img.src = img_src;
43619         
43620         this.is_empty = false;
43621     },
43622     
43623     selectImage: function()
43624     {
43625         this.fileEl().dom.click();
43626     },
43627     
43628     uploadImage: function(e)
43629     {
43630         var reader = new FileReader();
43631         
43632         reader.onload = function(e){
43633             var img = new Image();
43634             img.onload = function(){
43635                 this.reset();
43636                 this.canvasElCtx().drawImage(img, 0, 0);
43637             }.bind(this);
43638             img.src = e.target.result;
43639         }.bind(this);
43640         
43641         reader.readAsDataURL(e.target.files[0]);
43642     },
43643     
43644     // Bezier Point Constructor
43645     Point: (function () {
43646         function Point(x, y, time) {
43647             this.x = x;
43648             this.y = y;
43649             this.time = time || Date.now();
43650         }
43651         Point.prototype.distanceTo = function (start) {
43652             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43653         };
43654         Point.prototype.equals = function (other) {
43655             return this.x === other.x && this.y === other.y && this.time === other.time;
43656         };
43657         Point.prototype.velocityFrom = function (start) {
43658             return this.time !== start.time
43659             ? this.distanceTo(start) / (this.time - start.time)
43660             : 0;
43661         };
43662         return Point;
43663     }()),
43664     
43665     
43666     // Bezier Constructor
43667     Bezier: (function () {
43668         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43669             this.startPoint = startPoint;
43670             this.control2 = control2;
43671             this.control1 = control1;
43672             this.endPoint = endPoint;
43673             this.startWidth = startWidth;
43674             this.endWidth = endWidth;
43675         }
43676         Bezier.fromPoints = function (points, widths, scope) {
43677             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43678             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43679             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43680         };
43681         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43682             var dx1 = s1.x - s2.x;
43683             var dy1 = s1.y - s2.y;
43684             var dx2 = s2.x - s3.x;
43685             var dy2 = s2.y - s3.y;
43686             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43687             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43688             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43689             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43690             var dxm = m1.x - m2.x;
43691             var dym = m1.y - m2.y;
43692             var k = l2 / (l1 + l2);
43693             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43694             var tx = s2.x - cm.x;
43695             var ty = s2.y - cm.y;
43696             return {
43697                 c1: new scope.Point(m1.x + tx, m1.y + ty),
43698                 c2: new scope.Point(m2.x + tx, m2.y + ty)
43699             };
43700         };
43701         Bezier.prototype.length = function () {
43702             var steps = 10;
43703             var length = 0;
43704             var px;
43705             var py;
43706             for (var i = 0; i <= steps; i += 1) {
43707                 var t = i / steps;
43708                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43709                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43710                 if (i > 0) {
43711                     var xdiff = cx - px;
43712                     var ydiff = cy - py;
43713                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43714                 }
43715                 px = cx;
43716                 py = cy;
43717             }
43718             return length;
43719         };
43720         Bezier.prototype.point = function (t, start, c1, c2, end) {
43721             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43722             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43723             + (3.0 * c2 * (1.0 - t) * t * t)
43724             + (end * t * t * t);
43725         };
43726         return Bezier;
43727     }()),
43728     
43729     throttleStroke: function(fn, wait) {
43730       if (wait === void 0) { wait = 250; }
43731       var previous = 0;
43732       var timeout = null;
43733       var result;
43734       var storedContext;
43735       var storedArgs;
43736       var later = function () {
43737           previous = Date.now();
43738           timeout = null;
43739           result = fn.apply(storedContext, storedArgs);
43740           if (!timeout) {
43741               storedContext = null;
43742               storedArgs = [];
43743           }
43744       };
43745       return function wrapper() {
43746           var args = [];
43747           for (var _i = 0; _i < arguments.length; _i++) {
43748               args[_i] = arguments[_i];
43749           }
43750           var now = Date.now();
43751           var remaining = wait - (now - previous);
43752           storedContext = this;
43753           storedArgs = args;
43754           if (remaining <= 0 || remaining > wait) {
43755               if (timeout) {
43756                   clearTimeout(timeout);
43757                   timeout = null;
43758               }
43759               previous = now;
43760               result = fn.apply(storedContext, storedArgs);
43761               if (!timeout) {
43762                   storedContext = null;
43763                   storedArgs = [];
43764               }
43765           }
43766           else if (!timeout) {
43767               timeout = window.setTimeout(later, remaining);
43768           }
43769           return result;
43770       };
43771   }
43772   
43773 });
43774
43775  
43776
43777