roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<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>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     /**
366      * Fetch the element to display the tooltip on.
367      * @return {Roo.Element} defaults to this.el
368      */
369     tooltipEl : function()
370     {
371         return this.el;
372     },
373         
374     addxtype  : function(tree,cntr)
375     {
376         var cn = this;
377         
378         cn = Roo.factory(tree);
379         //Roo.log(['addxtype', cn]);
380            
381         cn.parentType = this.xtype; //??
382         cn.parentId = this.id;
383         
384         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385         if (typeof(cn.container_method) == 'string') {
386             cntr = cn.container_method;
387         }
388         
389         
390         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
391         
392         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
393         
394         var build_from_html =  Roo.XComponent.build_from_html;
395           
396         var is_body  = (tree.xtype == 'Body') ;
397           
398         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
399           
400         var self_cntr_el = Roo.get(this[cntr](false));
401         
402         // do not try and build conditional elements 
403         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
404             return false;
405         }
406         
407         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409                 return this.addxtypeChild(tree,cntr, is_body);
410             }
411             
412             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
413                 
414             if(echild){
415                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
416             }
417             
418             Roo.log('skipping render');
419             return cn;
420             
421         }
422         
423         var ret = false;
424         if (!build_from_html) {
425             return false;
426         }
427         
428         // this i think handles overlaying multiple children of the same type
429         // with the sam eelement.. - which might be buggy..
430         while (true) {
431             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
432             
433             if (!echild) {
434                 break;
435             }
436             
437             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
438                 break;
439             }
440             
441             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
442         }
443        
444         return ret;
445     },
446     
447     
448     addxtypeChild : function (tree, cntr, is_body)
449     {
450         Roo.debug && Roo.log('addxtypeChild:' + cntr);
451         var cn = this;
452         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
453         
454         
455         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456                     (typeof(tree['flexy:foreach']) != 'undefined');
457           
458     
459         
460         skip_children = false;
461         // render the element if it's not BODY.
462         if (!is_body) {
463             
464             // if parent was disabled, then do not try and create the children..
465             if(!this[cntr](true)){
466                 tree.items = [];
467                 return tree;
468             }
469            
470             cn = Roo.factory(tree);
471            
472             cn.parentType = this.xtype; //??
473             cn.parentId = this.id;
474             
475             var build_from_html =  Roo.XComponent.build_from_html;
476             
477             
478             // does the container contain child eleemnts with 'xtype' attributes.
479             // that match this xtype..
480             // note - when we render we create these as well..
481             // so we should check to see if body has xtype set.
482             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
483                
484                 var self_cntr_el = Roo.get(this[cntr](false));
485                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
486                 if (echild) { 
487                     //Roo.log(Roo.XComponent.build_from_html);
488                     //Roo.log("got echild:");
489                     //Roo.log(echild);
490                 }
491                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492                 // and are not displayed -this causes this to use up the wrong element when matching.
493                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
494                 
495                 
496                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
498                   
499                   
500                   
501                     cn.el = echild;
502                   //  Roo.log("GOT");
503                     //echild.dom.removeAttribute('xtype');
504                 } else {
505                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506                     Roo.debug && Roo.log(self_cntr_el);
507                     Roo.debug && Roo.log(echild);
508                     Roo.debug && Roo.log(cn);
509                 }
510             }
511            
512             
513            
514             // if object has flexy:if - then it may or may not be rendered.
515             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
516                 // skip a flexy if element.
517                 Roo.debug && Roo.log('skipping render');
518                 Roo.debug && Roo.log(tree);
519                 if (!cn.el) {
520                     Roo.debug && Roo.log('skipping all children');
521                     skip_children = true;
522                 }
523                 
524              } else {
525                  
526                 // actually if flexy:foreach is found, we really want to create 
527                 // multiple copies here...
528                 //Roo.log('render');
529                 //Roo.log(this[cntr]());
530                 // some elements do not have render methods.. like the layouts...
531                 /*
532                 if(this[cntr](true) === false){
533                     cn.items = [];
534                     return cn;
535                 }
536                 */
537                 cn.render && cn.render(this[cntr](true));
538                 
539              }
540             // then add the element..
541         }
542          
543         // handle the kids..
544         
545         var nitems = [];
546         /*
547         if (typeof (tree.menu) != 'undefined') {
548             tree.menu.parentType = cn.xtype;
549             tree.menu.triggerEl = cn.el;
550             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
551             
552         }
553         */
554         if (!tree.items || !tree.items.length) {
555             cn.items = nitems;
556             //Roo.log(["no children", this]);
557             
558             return cn;
559         }
560          
561         var items = tree.items;
562         delete tree.items;
563         
564         //Roo.log(items.length);
565             // add the items..
566         if (!skip_children) {    
567             for(var i =0;i < items.length;i++) {
568               //  Roo.log(['add child', items[i]]);
569                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
570             }
571         }
572         
573         cn.items = nitems;
574         
575         //Roo.log("fire childrenrendered");
576         
577         cn.fireEvent('childrenrendered', this);
578         
579         return cn;
580     },
581     
582     /**
583      * Set the element that will be used to show or hide
584      */
585     setVisibilityEl : function(el)
586     {
587         this.visibilityEl = el;
588     },
589     
590      /**
591      * Get the element that will be used to show or hide
592      */
593     getVisibilityEl : function()
594     {
595         if (typeof(this.visibilityEl) == 'object') {
596             return this.visibilityEl;
597         }
598         
599         if (typeof(this.visibilityEl) == 'string') {
600             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
601         }
602         
603         return this.getEl();
604     },
605     
606     /**
607      * Show a component - removes 'hidden' class
608      */
609     show : function()
610     {
611         if(!this.getVisibilityEl()){
612             return;
613         }
614          
615         this.getVisibilityEl().removeClass(['hidden','d-none']);
616         
617         this.fireEvent('show', this);
618         
619         
620     },
621     /**
622      * Hide a component - adds 'hidden' class
623      */
624     hide: function()
625     {
626         if(!this.getVisibilityEl()){
627             return;
628         }
629         
630         this.getVisibilityEl().addClass(['hidden','d-none']);
631         
632         this.fireEvent('hide', this);
633         
634     }
635 });
636
637  /*
638  * - LGPL
639  *
640  * element
641  * 
642  */
643
644 /**
645  * @class Roo.bootstrap.Element
646  * @extends Roo.bootstrap.Component
647  * Bootstrap Element class
648  * @cfg {String} html contents of the element
649  * @cfg {String} tag tag of the element
650  * @cfg {String} cls class of the element
651  * @cfg {Boolean} preventDefault (true|false) default false
652  * @cfg {Boolean} clickable (true|false) default false
653  * 
654  * @constructor
655  * Create a new Element
656  * @param {Object} config The config object
657  */
658
659 Roo.bootstrap.Element = function(config){
660     Roo.bootstrap.Element.superclass.constructor.call(this, config);
661     
662     this.addEvents({
663         // raw events
664         /**
665          * @event click
666          * When a element is chick
667          * @param {Roo.bootstrap.Element} this
668          * @param {Roo.EventObject} e
669          */
670         "click" : true 
671         
672       
673     });
674 };
675
676 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
677     
678     tag: 'div',
679     cls: '',
680     html: '',
681     preventDefault: false, 
682     clickable: false,
683     tapedTwice : false,
684     
685     getAutoCreate : function(){
686         
687         var cfg = {
688             tag: this.tag,
689             // cls: this.cls, double assign in parent class Component.js :: onRender
690             html: this.html
691         };
692         
693         return cfg;
694     },
695     
696     initEvents: function() 
697     {
698         Roo.bootstrap.Element.superclass.initEvents.call(this);
699         
700         if(this.clickable){
701             this.el.on('click', this.onClick, this);
702         }
703         
704         
705     },
706     
707     onClick : function(e)
708     {
709         if(this.preventDefault){
710             e.preventDefault();
711         }
712         
713         this.fireEvent('dblclick', this, e);
714     },
715     
716     
717     
718
719     
720     
721     getValue : function()
722     {
723         return this.el.dom.innerHTML;
724     },
725     
726     setValue : function(value)
727     {
728         this.el.dom.innerHTML = value;
729     }
730    
731 });
732
733  
734
735  /*
736  * - LGPL
737  *
738  * dropable area
739  * 
740  */
741
742 /**
743  * @class Roo.bootstrap.DropTarget
744  * @extends Roo.bootstrap.Element
745  * Bootstrap DropTarget class
746  
747  * @cfg {string} name dropable name
748  * 
749  * @constructor
750  * Create a new Dropable Area
751  * @param {Object} config The config object
752  */
753
754 Roo.bootstrap.DropTarget = function(config){
755     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
756     
757     this.addEvents({
758         // raw events
759         /**
760          * @event click
761          * When a element is chick
762          * @param {Roo.bootstrap.Element} this
763          * @param {Roo.EventObject} e
764          */
765         "drop" : true
766     });
767 };
768
769 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
770     
771     
772     getAutoCreate : function(){
773         
774          
775     },
776     
777     initEvents: function() 
778     {
779         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
780         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
781             ddGroup: this.name,
782             listeners : {
783                 drop : this.dragDrop.createDelegate(this),
784                 enter : this.dragEnter.createDelegate(this),
785                 out : this.dragOut.createDelegate(this),
786                 over : this.dragOver.createDelegate(this)
787             }
788             
789         });
790         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
791     },
792     
793     dragDrop : function(source,e,data)
794     {
795         // user has to decide how to impliment this.
796         Roo.log('drop');
797         Roo.log(this);
798         //this.fireEvent('drop', this, source, e ,data);
799         return false;
800     },
801     
802     dragEnter : function(n, dd, e, data)
803     {
804         // probably want to resize the element to match the dropped element..
805         Roo.log("enter");
806         this.originalSize = this.el.getSize();
807         this.el.setSize( n.el.getSize());
808         this.dropZone.DDM.refreshCache(this.name);
809         Roo.log([n, dd, e, data]);
810     },
811     
812     dragOut : function(value)
813     {
814         // resize back to normal
815         Roo.log("out");
816         this.el.setSize(this.originalSize);
817         this.dropZone.resetConstraints();
818     },
819     
820     dragOver : function()
821     {
822         // ??? do nothing?
823     }
824    
825 });
826
827  
828
829  /*
830  * - LGPL
831  *
832  * Body
833  *
834  */
835
836 /**
837  * @class Roo.bootstrap.Body
838  * @extends Roo.bootstrap.Component
839  * Bootstrap Body class
840  *
841  * @constructor
842  * Create a new body
843  * @param {Object} config The config object
844  */
845
846 Roo.bootstrap.Body = function(config){
847
848     config = config || {};
849
850     Roo.bootstrap.Body.superclass.constructor.call(this, config);
851     this.el = Roo.get(config.el ? config.el : document.body );
852     if (this.cls && this.cls.length) {
853         Roo.get(document.body).addClass(this.cls);
854     }
855 };
856
857 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
858
859     is_body : true,// just to make sure it's constructed?
860
861         autoCreate : {
862         cls: 'container'
863     },
864     onRender : function(ct, position)
865     {
866        /* Roo.log("Roo.bootstrap.Body - onRender");
867         if (this.cls && this.cls.length) {
868             Roo.get(document.body).addClass(this.cls);
869         }
870         // style??? xttr???
871         */
872     }
873
874
875
876
877 });
878 /*
879  * - LGPL
880  *
881  * button group
882  * 
883  */
884
885
886 /**
887  * @class Roo.bootstrap.ButtonGroup
888  * @extends Roo.bootstrap.Component
889  * Bootstrap ButtonGroup class
890  * @cfg {String} size lg | sm | xs (default empty normal)
891  * @cfg {String} align vertical | justified  (default none)
892  * @cfg {String} direction up | down (default down)
893  * @cfg {Boolean} toolbar false | true
894  * @cfg {Boolean} btn true | false
895  * 
896  * 
897  * @constructor
898  * Create a new Input
899  * @param {Object} config The config object
900  */
901
902 Roo.bootstrap.ButtonGroup = function(config){
903     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
904 };
905
906 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
907     
908     size: '',
909     align: '',
910     direction: '',
911     toolbar: false,
912     btn: true,
913
914     getAutoCreate : function(){
915         var cfg = {
916             cls: 'btn-group',
917             html : null
918         };
919         
920         cfg.html = this.html || cfg.html;
921         
922         if (this.toolbar) {
923             cfg = {
924                 cls: 'btn-toolbar',
925                 html: null
926             };
927             
928             return cfg;
929         }
930         
931         if (['vertical','justified'].indexOf(this.align)!==-1) {
932             cfg.cls = 'btn-group-' + this.align;
933             
934             if (this.align == 'justified') {
935                 console.log(this.items);
936             }
937         }
938         
939         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
940             cfg.cls += ' btn-group-' + this.size;
941         }
942         
943         if (this.direction == 'up') {
944             cfg.cls += ' dropup' ;
945         }
946         
947         return cfg;
948     },
949     /**
950      * Add a button to the group (similar to NavItem API.)
951      */
952     addItem : function(cfg)
953     {
954         var cn = new Roo.bootstrap.Button(cfg);
955         //this.register(cn);
956         cn.parentId = this.id;
957         cn.onRender(this.el, null);
958         return cn;
959     }
960    
961 });
962
963  /*
964  * - LGPL
965  *
966  * button
967  * 
968  */
969
970 /**
971  * @class Roo.bootstrap.Button
972  * @extends Roo.bootstrap.Component
973  * Bootstrap Button class
974  * @cfg {String} html The button content
975  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
976  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
977  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
978  * @cfg {String} size (lg|sm|xs)
979  * @cfg {String} tag (a|input|submit)
980  * @cfg {String} href empty or href
981  * @cfg {Boolean} disabled default false;
982  * @cfg {Boolean} isClose default false;
983  * @cfg {String} glyphicon depricated - use fa
984  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
985  * @cfg {String} badge text for badge
986  * @cfg {String} theme (default|glow)  
987  * @cfg {Boolean} inverse dark themed version
988  * @cfg {Boolean} toggle is it a slidy toggle button
989  * @cfg {Boolean} pressed   default null - if the button ahs active state
990  * @cfg {String} ontext text for on slidy toggle state
991  * @cfg {String} offtext text for off slidy toggle state
992  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
993  * @cfg {Boolean} removeClass remove the standard class..
994  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
995  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
996  * 
997  * @constructor
998  * Create a new button
999  * @param {Object} config The config object
1000  */
1001
1002
1003 Roo.bootstrap.Button = function(config){
1004     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1005     
1006     this.addEvents({
1007         // raw events
1008         /**
1009          * @event click
1010          * When a button is pressed
1011          * @param {Roo.bootstrap.Button} btn
1012          * @param {Roo.EventObject} e
1013          */
1014         "click" : true,
1015         /**
1016          * @event dblclick
1017          * When a button is double clicked
1018          * @param {Roo.bootstrap.Button} btn
1019          * @param {Roo.EventObject} e
1020          */
1021         "dblclick" : true,
1022          /**
1023          * @event toggle
1024          * After the button has been toggles
1025          * @param {Roo.bootstrap.Button} btn
1026          * @param {Roo.EventObject} e
1027          * @param {boolean} pressed (also available as button.pressed)
1028          */
1029         "toggle" : true
1030     });
1031 };
1032
1033 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1034     html: false,
1035     active: false,
1036     weight: '',
1037     badge_weight: '',
1038     outline : false,
1039     size: '',
1040     tag: 'button',
1041     href: '',
1042     disabled: false,
1043     isClose: false,
1044     glyphicon: '',
1045     fa: '',
1046     badge: '',
1047     theme: 'default',
1048     inverse: false,
1049     
1050     toggle: false,
1051     ontext: 'ON',
1052     offtext: 'OFF',
1053     defaulton: true,
1054     preventDefault: true,
1055     removeClass: false,
1056     name: false,
1057     target: false,
1058     group : false,
1059      
1060     pressed : null,
1061      
1062     
1063     getAutoCreate : function(){
1064         
1065         var cfg = {
1066             tag : 'button',
1067             cls : 'roo-button',
1068             html: ''
1069         };
1070         
1071         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1072             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1073             this.tag = 'button';
1074         } else {
1075             cfg.tag = this.tag;
1076         }
1077         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1078         
1079         if (this.toggle == true) {
1080             cfg={
1081                 tag: 'div',
1082                 cls: 'slider-frame roo-button',
1083                 cn: [
1084                     {
1085                         tag: 'span',
1086                         'data-on-text':'ON',
1087                         'data-off-text':'OFF',
1088                         cls: 'slider-button',
1089                         html: this.offtext
1090                     }
1091                 ]
1092             };
1093             // why are we validating the weights?
1094             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1095                 cfg.cls +=  ' ' + this.weight;
1096             }
1097             
1098             return cfg;
1099         }
1100         
1101         if (this.isClose) {
1102             cfg.cls += ' close';
1103             
1104             cfg["aria-hidden"] = true;
1105             
1106             cfg.html = "&times;";
1107             
1108             return cfg;
1109         }
1110              
1111         
1112         if (this.theme==='default') {
1113             cfg.cls = 'btn roo-button';
1114             
1115             //if (this.parentType != 'Navbar') {
1116             this.weight = this.weight.length ?  this.weight : 'default';
1117             //}
1118             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1119                 
1120                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1121                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1122                 cfg.cls += ' btn-' + outline + weight;
1123                 if (this.weight == 'default') {
1124                     // BC
1125                     cfg.cls += ' btn-' + this.weight;
1126                 }
1127             }
1128         } else if (this.theme==='glow') {
1129             
1130             cfg.tag = 'a';
1131             cfg.cls = 'btn-glow roo-button';
1132             
1133             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1134                 
1135                 cfg.cls += ' ' + this.weight;
1136             }
1137         }
1138    
1139         
1140         if (this.inverse) {
1141             this.cls += ' inverse';
1142         }
1143         
1144         
1145         if (this.active || this.pressed === true) {
1146             cfg.cls += ' active';
1147         }
1148         
1149         if (this.disabled) {
1150             cfg.disabled = 'disabled';
1151         }
1152         
1153         if (this.items) {
1154             Roo.log('changing to ul' );
1155             cfg.tag = 'ul';
1156             this.glyphicon = 'caret';
1157             if (Roo.bootstrap.version == 4) {
1158                 this.fa = 'caret-down';
1159             }
1160             
1161         }
1162         
1163         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1164          
1165         //gsRoo.log(this.parentType);
1166         if (this.parentType === 'Navbar' && !this.parent().bar) {
1167             Roo.log('changing to li?');
1168             
1169             cfg.tag = 'li';
1170             
1171             cfg.cls = '';
1172             cfg.cn =  [{
1173                 tag : 'a',
1174                 cls : 'roo-button',
1175                 html : this.html,
1176                 href : this.href || '#'
1177             }];
1178             if (this.menu) {
1179                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1180                 cfg.cls += ' dropdown';
1181             }   
1182             
1183             delete cfg.html;
1184             
1185         }
1186         
1187        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1188         
1189         if (this.glyphicon) {
1190             cfg.html = ' ' + cfg.html;
1191             
1192             cfg.cn = [
1193                 {
1194                     tag: 'span',
1195                     cls: 'glyphicon glyphicon-' + this.glyphicon
1196                 }
1197             ];
1198         }
1199         if (this.fa) {
1200             cfg.html = ' ' + cfg.html;
1201             
1202             cfg.cn = [
1203                 {
1204                     tag: 'i',
1205                     cls: 'fa fas fa-' + this.fa
1206                 }
1207             ];
1208         }
1209         
1210         if (this.badge) {
1211             cfg.html += ' ';
1212             
1213             cfg.tag = 'a';
1214             
1215 //            cfg.cls='btn roo-button';
1216             
1217             cfg.href=this.href;
1218             
1219             var value = cfg.html;
1220             
1221             if(this.glyphicon){
1222                 value = {
1223                     tag: 'span',
1224                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1225                     html: this.html
1226                 };
1227             }
1228             if(this.fa){
1229                 value = {
1230                     tag: 'i',
1231                     cls: 'fa fas fa-' + this.fa,
1232                     html: this.html
1233                 };
1234             }
1235             
1236             var bw = this.badge_weight.length ? this.badge_weight :
1237                 (this.weight.length ? this.weight : 'secondary');
1238             bw = bw == 'default' ? 'secondary' : bw;
1239             
1240             cfg.cn = [
1241                 value,
1242                 {
1243                     tag: 'span',
1244                     cls: 'badge badge-' + bw,
1245                     html: this.badge
1246                 }
1247             ];
1248             
1249             cfg.html='';
1250         }
1251         
1252         if (this.menu) {
1253             cfg.cls += ' dropdown';
1254             cfg.html = typeof(cfg.html) != 'undefined' ?
1255                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1256         }
1257         
1258         if (cfg.tag !== 'a' && this.href !== '') {
1259             throw "Tag must be a to set href.";
1260         } else if (this.href.length > 0) {
1261             cfg.href = this.href;
1262         }
1263         
1264         if(this.removeClass){
1265             cfg.cls = '';
1266         }
1267         
1268         if(this.target){
1269             cfg.target = this.target;
1270         }
1271         
1272         return cfg;
1273     },
1274     initEvents: function() {
1275        // Roo.log('init events?');
1276 //        Roo.log(this.el.dom);
1277         // add the menu...
1278         
1279         if (typeof (this.menu) != 'undefined') {
1280             this.menu.parentType = this.xtype;
1281             this.menu.triggerEl = this.el;
1282             this.addxtype(Roo.apply({}, this.menu));
1283         }
1284
1285
1286         if (this.el.hasClass('roo-button')) {
1287              this.el.on('click', this.onClick, this);
1288              this.el.on('dblclick', this.onDblClick, this);
1289         } else {
1290              this.el.select('.roo-button').on('click', this.onClick, this);
1291              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1292              
1293         }
1294         // why?
1295         if(this.removeClass){
1296             this.el.on('click', this.onClick, this);
1297         }
1298         
1299         if (this.group === true) {
1300              if (this.pressed === false || this.pressed === true) {
1301                 // nothing
1302             } else {
1303                 this.pressed = false;
1304                 this.setActive(this.pressed);
1305             }
1306             
1307         }
1308         
1309         this.el.enableDisplayMode();
1310         
1311     },
1312     onClick : function(e)
1313     {
1314         if (this.disabled) {
1315             return;
1316         }
1317         
1318         Roo.log('button on click ');
1319         if(this.preventDefault){
1320             e.preventDefault();
1321         }
1322         
1323         if (this.group) {
1324             if (this.pressed) {
1325                 // do nothing -
1326                 return;
1327             }
1328             this.setActive(true);
1329             var pi = this.parent().items;
1330             for (var i = 0;i < pi.length;i++) {
1331                 if (this == pi[i]) {
1332                     continue;
1333                 }
1334                 if (pi[i].el.hasClass('roo-button')) {
1335                     pi[i].setActive(false);
1336                 }
1337             }
1338             this.fireEvent('click', this, e);            
1339             return;
1340         }
1341         
1342         if (this.pressed === true || this.pressed === false) {
1343             this.toggleActive(e);
1344         }
1345         
1346         
1347         this.fireEvent('click', this, e);
1348     },
1349     onDblClick: function(e)
1350     {
1351         if (this.disabled) {
1352             return;
1353         }
1354         if(this.preventDefault){
1355             e.preventDefault();
1356         }
1357         this.fireEvent('dblclick', this, e);
1358     },
1359     /**
1360      * Enables this button
1361      */
1362     enable : function()
1363     {
1364         this.disabled = false;
1365         this.el.removeClass('disabled');
1366         this.el.dom.removeAttribute("disabled");
1367     },
1368     
1369     /**
1370      * Disable this button
1371      */
1372     disable : function()
1373     {
1374         this.disabled = true;
1375         this.el.addClass('disabled');
1376         this.el.attr("disabled", "disabled")
1377     },
1378      /**
1379      * sets the active state on/off, 
1380      * @param {Boolean} state (optional) Force a particular state
1381      */
1382     setActive : function(v) {
1383         
1384         this.el[v ? 'addClass' : 'removeClass']('active');
1385         this.pressed = v;
1386     },
1387      /**
1388      * toggles the current active state 
1389      */
1390     toggleActive : function(e)
1391     {
1392         this.setActive(!this.pressed); // this modifies pressed...
1393         this.fireEvent('toggle', this, e, this.pressed);
1394     },
1395      /**
1396      * get the current active state
1397      * @return {boolean} true if it's active
1398      */
1399     isActive : function()
1400     {
1401         return this.el.hasClass('active');
1402     },
1403     /**
1404      * set the text of the first selected button
1405      */
1406     setText : function(str)
1407     {
1408         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1409     },
1410     /**
1411      * get the text of the first selected button
1412      */
1413     getText : function()
1414     {
1415         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1416     },
1417     
1418     setWeight : function(str)
1419     {
1420         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1421         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1422         this.weight = str;
1423         var outline = this.outline ? 'outline-' : '';
1424         if (str == 'default') {
1425             this.el.addClass('btn-default btn-outline-secondary');        
1426             return;
1427         }
1428         this.el.addClass('btn-' + outline + str);        
1429     }
1430     
1431     
1432 });
1433 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1434
1435 Roo.bootstrap.Button.weights = [
1436     'default',
1437     'secondary' ,
1438     'primary',
1439     'success',
1440     'info',
1441     'warning',
1442     'danger',
1443     'link',
1444     'light',
1445     'dark'              
1446    
1447 ];/*
1448  * - LGPL
1449  *
1450  * column
1451  * 
1452  */
1453
1454 /**
1455  * @class Roo.bootstrap.Column
1456  * @extends Roo.bootstrap.Component
1457  * Bootstrap Column class
1458  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1459  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1460  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1461  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1462  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1463  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1464  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1465  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1466  *
1467  * 
1468  * @cfg {Boolean} hidden (true|false) hide the element
1469  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1470  * @cfg {String} fa (ban|check|...) font awesome icon
1471  * @cfg {Number} fasize (1|2|....) font awsome size
1472
1473  * @cfg {String} icon (info-sign|check|...) glyphicon name
1474
1475  * @cfg {String} html content of column.
1476  * 
1477  * @constructor
1478  * Create a new Column
1479  * @param {Object} config The config object
1480  */
1481
1482 Roo.bootstrap.Column = function(config){
1483     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1484 };
1485
1486 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1487     
1488     xs: false,
1489     sm: false,
1490     md: false,
1491     lg: false,
1492     xsoff: false,
1493     smoff: false,
1494     mdoff: false,
1495     lgoff: false,
1496     html: '',
1497     offset: 0,
1498     alert: false,
1499     fa: false,
1500     icon : false,
1501     hidden : false,
1502     fasize : 1,
1503     
1504     getAutoCreate : function(){
1505         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1506         
1507         cfg = {
1508             tag: 'div',
1509             cls: 'column'
1510         };
1511         
1512         var settings=this;
1513         var sizes =   ['xs','sm','md','lg'];
1514         sizes.map(function(size ,ix){
1515             //Roo.log( size + ':' + settings[size]);
1516             
1517             if (settings[size+'off'] !== false) {
1518                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1519             }
1520             
1521             if (settings[size] === false) {
1522                 return;
1523             }
1524             
1525             if (!settings[size]) { // 0 = hidden
1526                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1527                 // bootsrap4
1528                 for (var i = ix; i > -1; i--) {
1529                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1530                 }
1531                 
1532                 
1533                 return;
1534             }
1535             cfg.cls += ' col-' + size + '-' + settings[size] + (
1536                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1537             );
1538             
1539         });
1540         
1541         if (this.hidden) {
1542             cfg.cls += ' hidden';
1543         }
1544         
1545         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1546             cfg.cls +=' alert alert-' + this.alert;
1547         }
1548         
1549         
1550         if (this.html.length) {
1551             cfg.html = this.html;
1552         }
1553         if (this.fa) {
1554             var fasize = '';
1555             if (this.fasize > 1) {
1556                 fasize = ' fa-' + this.fasize + 'x';
1557             }
1558             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1559             
1560             
1561         }
1562         if (this.icon) {
1563             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1564         }
1565         
1566         return cfg;
1567     }
1568    
1569 });
1570
1571  
1572
1573  /*
1574  * - LGPL
1575  *
1576  * page container.
1577  * 
1578  */
1579
1580
1581 /**
1582  * @class Roo.bootstrap.Container
1583  * @extends Roo.bootstrap.Component
1584  * Bootstrap Container class
1585  * @cfg {Boolean} jumbotron is it a jumbotron element
1586  * @cfg {String} html content of element
1587  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1588  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1589  * @cfg {String} header content of header (for panel)
1590  * @cfg {String} footer content of footer (for panel)
1591  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1592  * @cfg {String} tag (header|aside|section) type of HTML tag.
1593  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1594  * @cfg {String} fa font awesome icon
1595  * @cfg {String} icon (info-sign|check|...) glyphicon name
1596  * @cfg {Boolean} hidden (true|false) hide the element
1597  * @cfg {Boolean} expandable (true|false) default false
1598  * @cfg {Boolean} expanded (true|false) default true
1599  * @cfg {String} rheader contet on the right of header
1600  * @cfg {Boolean} clickable (true|false) default false
1601
1602  *     
1603  * @constructor
1604  * Create a new Container
1605  * @param {Object} config The config object
1606  */
1607
1608 Roo.bootstrap.Container = function(config){
1609     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1610     
1611     this.addEvents({
1612         // raw events
1613          /**
1614          * @event expand
1615          * After the panel has been expand
1616          * 
1617          * @param {Roo.bootstrap.Container} this
1618          */
1619         "expand" : true,
1620         /**
1621          * @event collapse
1622          * After the panel has been collapsed
1623          * 
1624          * @param {Roo.bootstrap.Container} this
1625          */
1626         "collapse" : true,
1627         /**
1628          * @event click
1629          * When a element is chick
1630          * @param {Roo.bootstrap.Container} this
1631          * @param {Roo.EventObject} e
1632          */
1633         "click" : true
1634     });
1635 };
1636
1637 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1638     
1639     jumbotron : false,
1640     well: '',
1641     panel : '',
1642     header: '',
1643     footer : '',
1644     sticky: '',
1645     tag : false,
1646     alert : false,
1647     fa: false,
1648     icon : false,
1649     expandable : false,
1650     rheader : '',
1651     expanded : true,
1652     clickable: false,
1653   
1654      
1655     getChildContainer : function() {
1656         
1657         if(!this.el){
1658             return false;
1659         }
1660         
1661         if (this.panel.length) {
1662             return this.el.select('.panel-body',true).first();
1663         }
1664         
1665         return this.el;
1666     },
1667     
1668     
1669     getAutoCreate : function(){
1670         
1671         var cfg = {
1672             tag : this.tag || 'div',
1673             html : '',
1674             cls : ''
1675         };
1676         if (this.jumbotron) {
1677             cfg.cls = 'jumbotron';
1678         }
1679         
1680         
1681         
1682         // - this is applied by the parent..
1683         //if (this.cls) {
1684         //    cfg.cls = this.cls + '';
1685         //}
1686         
1687         if (this.sticky.length) {
1688             
1689             var bd = Roo.get(document.body);
1690             if (!bd.hasClass('bootstrap-sticky')) {
1691                 bd.addClass('bootstrap-sticky');
1692                 Roo.select('html',true).setStyle('height', '100%');
1693             }
1694              
1695             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1696         }
1697         
1698         
1699         if (this.well.length) {
1700             switch (this.well) {
1701                 case 'lg':
1702                 case 'sm':
1703                     cfg.cls +=' well well-' +this.well;
1704                     break;
1705                 default:
1706                     cfg.cls +=' well';
1707                     break;
1708             }
1709         }
1710         
1711         if (this.hidden) {
1712             cfg.cls += ' hidden';
1713         }
1714         
1715         
1716         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1717             cfg.cls +=' alert alert-' + this.alert;
1718         }
1719         
1720         var body = cfg;
1721         
1722         if (this.panel.length) {
1723             cfg.cls += ' panel panel-' + this.panel;
1724             cfg.cn = [];
1725             if (this.header.length) {
1726                 
1727                 var h = [];
1728                 
1729                 if(this.expandable){
1730                     
1731                     cfg.cls = cfg.cls + ' expandable';
1732                     
1733                     h.push({
1734                         tag: 'i',
1735                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1736                     });
1737                     
1738                 }
1739                 
1740                 h.push(
1741                     {
1742                         tag: 'span',
1743                         cls : 'panel-title',
1744                         html : (this.expandable ? '&nbsp;' : '') + this.header
1745                     },
1746                     {
1747                         tag: 'span',
1748                         cls: 'panel-header-right',
1749                         html: this.rheader
1750                     }
1751                 );
1752                 
1753                 cfg.cn.push({
1754                     cls : 'panel-heading',
1755                     style : this.expandable ? 'cursor: pointer' : '',
1756                     cn : h
1757                 });
1758                 
1759             }
1760             
1761             body = false;
1762             cfg.cn.push({
1763                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1764                 html : this.html
1765             });
1766             
1767             
1768             if (this.footer.length) {
1769                 cfg.cn.push({
1770                     cls : 'panel-footer',
1771                     html : this.footer
1772                     
1773                 });
1774             }
1775             
1776         }
1777         
1778         if (body) {
1779             body.html = this.html || cfg.html;
1780             // prefix with the icons..
1781             if (this.fa) {
1782                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1783             }
1784             if (this.icon) {
1785                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1786             }
1787             
1788             
1789         }
1790         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1791             cfg.cls =  'container';
1792         }
1793         
1794         return cfg;
1795     },
1796     
1797     initEvents: function() 
1798     {
1799         if(this.expandable){
1800             var headerEl = this.headerEl();
1801         
1802             if(headerEl){
1803                 headerEl.on('click', this.onToggleClick, this);
1804             }
1805         }
1806         
1807         if(this.clickable){
1808             this.el.on('click', this.onClick, this);
1809         }
1810         
1811     },
1812     
1813     onToggleClick : function()
1814     {
1815         var headerEl = this.headerEl();
1816         
1817         if(!headerEl){
1818             return;
1819         }
1820         
1821         if(this.expanded){
1822             this.collapse();
1823             return;
1824         }
1825         
1826         this.expand();
1827     },
1828     
1829     expand : function()
1830     {
1831         if(this.fireEvent('expand', this)) {
1832             
1833             this.expanded = true;
1834             
1835             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1836             
1837             this.el.select('.panel-body',true).first().removeClass('hide');
1838             
1839             var toggleEl = this.toggleEl();
1840
1841             if(!toggleEl){
1842                 return;
1843             }
1844
1845             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1846         }
1847         
1848     },
1849     
1850     collapse : function()
1851     {
1852         if(this.fireEvent('collapse', this)) {
1853             
1854             this.expanded = false;
1855             
1856             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1857             this.el.select('.panel-body',true).first().addClass('hide');
1858         
1859             var toggleEl = this.toggleEl();
1860
1861             if(!toggleEl){
1862                 return;
1863             }
1864
1865             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1866         }
1867     },
1868     
1869     toggleEl : function()
1870     {
1871         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1872             return;
1873         }
1874         
1875         return this.el.select('.panel-heading .fa',true).first();
1876     },
1877     
1878     headerEl : function()
1879     {
1880         if(!this.el || !this.panel.length || !this.header.length){
1881             return;
1882         }
1883         
1884         return this.el.select('.panel-heading',true).first()
1885     },
1886     
1887     bodyEl : function()
1888     {
1889         if(!this.el || !this.panel.length){
1890             return;
1891         }
1892         
1893         return this.el.select('.panel-body',true).first()
1894     },
1895     
1896     titleEl : function()
1897     {
1898         if(!this.el || !this.panel.length || !this.header.length){
1899             return;
1900         }
1901         
1902         return this.el.select('.panel-title',true).first();
1903     },
1904     
1905     setTitle : function(v)
1906     {
1907         var titleEl = this.titleEl();
1908         
1909         if(!titleEl){
1910             return;
1911         }
1912         
1913         titleEl.dom.innerHTML = v;
1914     },
1915     
1916     getTitle : function()
1917     {
1918         
1919         var titleEl = this.titleEl();
1920         
1921         if(!titleEl){
1922             return '';
1923         }
1924         
1925         return titleEl.dom.innerHTML;
1926     },
1927     
1928     setRightTitle : function(v)
1929     {
1930         var t = this.el.select('.panel-header-right',true).first();
1931         
1932         if(!t){
1933             return;
1934         }
1935         
1936         t.dom.innerHTML = v;
1937     },
1938     
1939     onClick : function(e)
1940     {
1941         e.preventDefault();
1942         
1943         this.fireEvent('click', this, e);
1944     }
1945 });
1946
1947  /*
1948  *  - LGPL
1949  *
1950  *  This is BS4's Card element.. - similar to our containers probably..
1951  * 
1952  */
1953 /**
1954  * @class Roo.bootstrap.Card
1955  * @extends Roo.bootstrap.Component
1956  * Bootstrap Card class
1957  *
1958  *
1959  * possible... may not be implemented..
1960  * @cfg {String} header_image  src url of image.
1961  * @cfg {String|Object} header
1962  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1963  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1964  * 
1965  * @cfg {String} title
1966  * @cfg {String} subtitle
1967  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1968  * @cfg {String} footer
1969  
1970  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1971  * 
1972  * @cfg {String} margin (0|1|2|3|4|5|auto)
1973  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1974  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1975  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1976  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1977  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1978  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1979  *
1980  * @cfg {String} padding (0|1|2|3|4|5)
1981  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1982  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1983  * @cfg {String} padding_left (0|1|2|3|4|5)
1984  * @cfg {String} padding_right (0|1|2|3|4|5)
1985  * @cfg {String} padding_x (0|1|2|3|4|5)
1986  * @cfg {String} padding_y (0|1|2|3|4|5)
1987  *
1988  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1989  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1990  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1991  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1992  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1993  
1994  * @config {Boolean} dragable  if this card can be dragged.
1995  * @config {String} drag_group  group for drag
1996  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1997  * @config {String} drop_group  group for drag
1998  * 
1999  * @config {Boolean} collapsable can the body be collapsed.
2000  * @config {Boolean} collapsed is the body collapsed when rendered...
2001  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2002  * @config {Boolean} rotated is the body rotated when rendered...
2003  * 
2004  * @constructor
2005  * Create a new Container
2006  * @param {Object} config The config object
2007  */
2008
2009 Roo.bootstrap.Card = function(config){
2010     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2011     
2012     this.addEvents({
2013          // raw events
2014         /**
2015          * @event drop
2016          * When a element a card is dropped
2017          * @param {Roo.bootstrap.Card} this
2018          *
2019          * 
2020          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2021          * @param {String} position 'above' or 'below'
2022          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2023         
2024          */
2025         'drop' : true,
2026          /**
2027          * @event rotate
2028          * When a element a card is rotate
2029          * @param {Roo.bootstrap.Card} this
2030          * @param {Roo.Element} n the node being dropped?
2031          * @param {Boolean} rotate status
2032          */
2033         'rotate' : true,
2034         /**
2035          * @event cardover
2036          * When a card element is dragged over ready to drop (return false to block dropable)
2037          * @param {Roo.bootstrap.Card} this
2038          * @param {Object} data from dragdrop 
2039          */
2040          'cardover' : true
2041          
2042     });
2043 };
2044
2045
2046 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2047     
2048     
2049     weight : '',
2050     
2051     margin: '', /// may be better in component?
2052     margin_top: '', 
2053     margin_bottom: '', 
2054     margin_left: '',
2055     margin_right: '',
2056     margin_x: '',
2057     margin_y: '',
2058     
2059     padding : '',
2060     padding_top: '', 
2061     padding_bottom: '', 
2062     padding_left: '',
2063     padding_right: '',
2064     padding_x: '',
2065     padding_y: '',
2066     
2067     display: '', 
2068     display_xs: '', 
2069     display_sm: '', 
2070     display_lg: '',
2071     display_xl: '',
2072  
2073     header_image  : '',
2074     header : '',
2075     header_size : 0,
2076     title : '',
2077     subtitle : '',
2078     html : '',
2079     footer: '',
2080
2081     collapsable : false,
2082     collapsed : false,
2083     rotateable : false,
2084     rotated : false,
2085     
2086     dragable : false,
2087     drag_group : false,
2088     dropable : false,
2089     drop_group : false,
2090     childContainer : false,
2091     dropEl : false, /// the dom placeholde element that indicates drop location.
2092     containerEl: false, // body container
2093     bodyEl: false, // card-body
2094     headerContainerEl : false, //
2095     headerEl : false,
2096     header_imageEl : false,
2097     
2098     layoutCls : function()
2099     {
2100         var cls = '';
2101         var t = this;
2102         Roo.log(this.margin_bottom.length);
2103         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2104             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2105             
2106             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2107                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2108             }
2109             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2110                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2111             }
2112         });
2113         
2114         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2115             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2116                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2117             }
2118         });
2119         
2120         // more generic support?
2121         if (this.hidden) {
2122             cls += ' d-none';
2123         }
2124         
2125         return cls;
2126     },
2127  
2128        // Roo.log("Call onRender: " + this.xtype);
2129         /*  We are looking at something like this.
2130 <div class="card">
2131     <img src="..." class="card-img-top" alt="...">
2132     <div class="card-body">
2133         <h5 class="card-title">Card title</h5>
2134          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2135
2136         >> this bit is really the body...
2137         <div> << we will ad dthis in hopefully it will not break shit.
2138         
2139         ** card text does not actually have any styling...
2140         
2141             <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>
2142         
2143         </div> <<
2144           <a href="#" class="card-link">Card link</a>
2145           
2146     </div>
2147     <div class="card-footer">
2148         <small class="text-muted">Last updated 3 mins ago</small>
2149     </div>
2150 </div>
2151          */
2152     getAutoCreate : function(){
2153         
2154         var cfg = {
2155             tag : 'div',
2156             cls : 'card',
2157             cn : [ ]
2158         };
2159         
2160         if (this.weight.length && this.weight != 'light') {
2161             cfg.cls += ' text-white';
2162         } else {
2163             cfg.cls += ' text-dark'; // need as it's nested..
2164         }
2165         if (this.weight.length) {
2166             cfg.cls += ' bg-' + this.weight;
2167         }
2168         
2169         cfg.cls += ' ' + this.layoutCls(); 
2170         
2171         var hdr = false;
2172         var hdr_ctr = false;
2173         if (this.header.length) {
2174             hdr = {
2175                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2176                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2177                 cn : []
2178             };
2179             cfg.cn.push(hdr);
2180             hdr_ctr = hdr;
2181         } else {
2182             hdr = {
2183                 tag : 'div',
2184                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2185                 cn : []
2186             };
2187             cfg.cn.push(hdr);
2188             hdr_ctr = hdr;
2189         }
2190         if (this.collapsable) {
2191             hdr_ctr = {
2192             tag : 'a',
2193             cls : 'd-block user-select-none',
2194             cn: [
2195                     {
2196                         tag: 'i',
2197                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2198                     }
2199                    
2200                 ]
2201             };
2202             hdr.cn.push(hdr_ctr);
2203         }
2204         
2205         hdr_ctr.cn.push(        {
2206             tag: 'span',
2207             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2208             html : this.header
2209         });
2210         
2211         
2212         if (this.header_image.length) {
2213             cfg.cn.push({
2214                 tag : 'img',
2215                 cls : 'card-img-top',
2216                 src: this.header_image // escape?
2217             });
2218         } else {
2219             cfg.cn.push({
2220                     tag : 'div',
2221                     cls : 'card-img-top d-none' 
2222                 });
2223         }
2224             
2225         var body = {
2226             tag : 'div',
2227             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2228             cn : []
2229         };
2230         var obody = body;
2231         if (this.collapsable || this.rotateable) {
2232             obody = {
2233                 tag: 'div',
2234                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2235                 cn : [  body ]
2236             };
2237         }
2238         
2239         cfg.cn.push(obody);
2240         
2241         if (this.title.length) {
2242             body.cn.push({
2243                 tag : 'div',
2244                 cls : 'card-title',
2245                 src: this.title // escape?
2246             });
2247         }  
2248         
2249         if (this.subtitle.length) {
2250             body.cn.push({
2251                 tag : 'div',
2252                 cls : 'card-title',
2253                 src: this.subtitle // escape?
2254             });
2255         }
2256         
2257         body.cn.push({
2258             tag : 'div',
2259             cls : 'roo-card-body-ctr'
2260         });
2261         
2262         if (this.html.length) {
2263             body.cn.push({
2264                 tag: 'div',
2265                 html : this.html
2266             });
2267         }
2268         // fixme ? handle objects?
2269         
2270         if (this.footer.length) {
2271            
2272             cfg.cn.push({
2273                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2274                 html : this.footer
2275             });
2276             
2277         } else {
2278             cfg.cn.push({cls : 'card-footer d-none'});
2279         }
2280         
2281         // footer...
2282         
2283         return cfg;
2284     },
2285     
2286     
2287     getCardHeader : function()
2288     {
2289         var  ret = this.el.select('.card-header',true).first();
2290         if (ret.hasClass('d-none')) {
2291             ret.removeClass('d-none');
2292         }
2293         
2294         return ret;
2295     },
2296     getCardFooter : function()
2297     {
2298         var  ret = this.el.select('.card-footer',true).first();
2299         if (ret.hasClass('d-none')) {
2300             ret.removeClass('d-none');
2301         }
2302         
2303         return ret;
2304     },
2305     getCardImageTop : function()
2306     {
2307         var  ret = this.header_imageEl;
2308         if (ret.hasClass('d-none')) {
2309             ret.removeClass('d-none');
2310         }
2311             
2312         return ret;
2313     },
2314     
2315     getChildContainer : function()
2316     {
2317         
2318         if(!this.el){
2319             return false;
2320         }
2321         return this.el.select('.roo-card-body-ctr',true).first();    
2322     },
2323     
2324     initEvents: function() 
2325     {
2326         this.bodyEl = this.el.select('.card-body',true).first(); 
2327         this.containerEl = this.getChildContainer();
2328         if(this.dragable){
2329             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2330                     containerScroll: true,
2331                     ddGroup: this.drag_group || 'default_card_drag_group'
2332             });
2333             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2334         }
2335         if (this.dropable) {
2336             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2337                 containerScroll: true,
2338                 ddGroup: this.drop_group || 'default_card_drag_group'
2339             });
2340             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2341             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2342             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2343             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2344             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2345         }
2346         
2347         if (this.collapsable) {
2348             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2349         }
2350         if (this.rotateable) {
2351             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2352         }
2353         this.collapsableEl = this.el.select('.roo-collapsable').first();
2354          
2355         this.footerEl = this.el.select('.card-footer').first();
2356         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2357         this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2358         this.headerEl = this.el.select('.card-header',true).first();
2359         
2360         if (this.rotated) {
2361             this.el.addClass('roo-card-rotated');
2362             this.fireEvent('rotate', this, true);
2363         }
2364         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2365         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2366         
2367     },
2368     getDragData : function(e)
2369     {
2370         var target = this.getEl();
2371         if (target) {
2372             //this.handleSelection(e);
2373             
2374             var dragData = {
2375                 source: this,
2376                 copy: false,
2377                 nodes: this.getEl(),
2378                 records: []
2379             };
2380             
2381             
2382             dragData.ddel = target.dom ;    // the div element
2383             Roo.log(target.getWidth( ));
2384             dragData.ddel.style.width = target.getWidth() + 'px';
2385             
2386             return dragData;
2387         }
2388         return false;
2389     },
2390     /**
2391     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2392     *    whole Element becomes the target, and this causes the drop gesture to append.
2393     *
2394     *    Returns an object:
2395     *     {
2396            
2397            position : 'below' or 'above'
2398            card  : relateive to card OBJECT (or true for no cards listed)
2399            items_n : relative to nth item in list
2400            card_n : relative to  nth card in list
2401     }
2402     *
2403     *    
2404     */
2405     getTargetFromEvent : function(e, dragged_card_el)
2406     {
2407         var target = e.getTarget();
2408         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2409             target = target.parentNode;
2410         }
2411         
2412         var ret = {
2413             position: '',
2414             cards : [],
2415             card_n : -1,
2416             items_n : -1,
2417             card : false 
2418         };
2419         
2420         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2421         // see if target is one of the 'cards'...
2422         
2423         
2424         //Roo.log(this.items.length);
2425         var pos = false;
2426         
2427         var last_card_n = 0;
2428         var cards_len  = 0;
2429         for (var i = 0;i< this.items.length;i++) {
2430             
2431             if (!this.items[i].el.hasClass('card')) {
2432                  continue;
2433             }
2434             pos = this.getDropPoint(e, this.items[i].el.dom);
2435             
2436             cards_len = ret.cards.length;
2437             //Roo.log(this.items[i].el.dom.id);
2438             ret.cards.push(this.items[i]);
2439             last_card_n  = i;
2440             if (ret.card_n < 0 && pos == 'above') {
2441                 ret.position = cards_len > 0 ? 'below' : pos;
2442                 ret.items_n = i > 0 ? i - 1 : 0;
2443                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2444                 ret.card = ret.cards[ret.card_n];
2445             }
2446         }
2447         if (!ret.cards.length) {
2448             ret.card = true;
2449             ret.position = 'below';
2450             ret.items_n;
2451             return ret;
2452         }
2453         // could not find a card.. stick it at the end..
2454         if (ret.card_n < 0) {
2455             ret.card_n = last_card_n;
2456             ret.card = ret.cards[last_card_n];
2457             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2458             ret.position = 'below';
2459         }
2460         
2461         if (this.items[ret.items_n].el == dragged_card_el) {
2462             return false;
2463         }
2464         
2465         if (ret.position == 'below') {
2466             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2467             
2468             if (card_after  && card_after.el == dragged_card_el) {
2469                 return false;
2470             }
2471             return ret;
2472         }
2473         
2474         // its's after ..
2475         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2476         
2477         if (card_before  && card_before.el == dragged_card_el) {
2478             return false;
2479         }
2480         
2481         return ret;
2482     },
2483     
2484     onNodeEnter : function(n, dd, e, data){
2485         return false;
2486     },
2487     onNodeOver : function(n, dd, e, data)
2488     {
2489        
2490         var target_info = this.getTargetFromEvent(e,data.source.el);
2491         if (target_info === false) {
2492             this.dropPlaceHolder('hide');
2493             return false;
2494         }
2495         Roo.log(['getTargetFromEvent', target_info ]);
2496         
2497         
2498         if (this.fireEvent('cardover', this, [ data ]) === false) {
2499             return false;
2500         }
2501         
2502         this.dropPlaceHolder('show', target_info,data);
2503         
2504         return false; 
2505     },
2506     onNodeOut : function(n, dd, e, data){
2507         this.dropPlaceHolder('hide');
2508      
2509     },
2510     onNodeDrop : function(n, dd, e, data)
2511     {
2512         
2513         // call drop - return false if
2514         
2515         // this could actually fail - if the Network drops..
2516         // we will ignore this at present..- client should probably reload
2517         // the whole set of cards if stuff like that fails.
2518         
2519         
2520         var info = this.getTargetFromEvent(e,data.source.el);
2521         if (info === false) {
2522             return false;
2523         }
2524         this.dropPlaceHolder('hide');
2525   
2526           
2527     
2528         this.acceptCard(data.source, info.position, info.card, info.items_n);
2529         return true;
2530          
2531     },
2532     firstChildCard : function()
2533     {
2534         for (var i = 0;i< this.items.length;i++) {
2535             
2536             if (!this.items[i].el.hasClass('card')) {
2537                  continue;
2538             }
2539             return this.items[i];
2540         }
2541         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2542     },
2543     /**
2544      * accept card
2545      *
2546      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2547      */
2548     acceptCard : function(move_card,  position, next_to_card )
2549     {
2550         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2551             return false;
2552         }
2553         
2554         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2555         
2556         move_card.parent().removeCard(move_card);
2557         
2558         
2559         var dom = move_card.el.dom;
2560         dom.style.width = ''; // clear with - which is set by drag.
2561         
2562         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2563             var cardel = next_to_card.el.dom;
2564             
2565             if (position == 'above' ) {
2566                 cardel.parentNode.insertBefore(dom, cardel);
2567             } else if (cardel.nextSibling) {
2568                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2569             } else {
2570                 cardel.parentNode.append(dom);
2571             }
2572         } else {
2573             // card container???
2574             this.containerEl.dom.append(dom);
2575         }
2576         
2577         //FIXME HANDLE card = true 
2578         
2579         // add this to the correct place in items.
2580         
2581         // remove Card from items.
2582         
2583        
2584         if (this.items.length) {
2585             var nitems = [];
2586             //Roo.log([info.items_n, info.position, this.items.length]);
2587             for (var i =0; i < this.items.length; i++) {
2588                 if (i == to_items_n && position == 'above') {
2589                     nitems.push(move_card);
2590                 }
2591                 nitems.push(this.items[i]);
2592                 if (i == to_items_n && position == 'below') {
2593                     nitems.push(move_card);
2594                 }
2595             }
2596             this.items = nitems;
2597             Roo.log(this.items);
2598         } else {
2599             this.items.push(move_card);
2600         }
2601         
2602         move_card.parentId = this.id;
2603         
2604         return true;
2605         
2606         
2607     },
2608     removeCard : function(c)
2609     {
2610         this.items = this.items.filter(function(e) { return e != c });
2611  
2612         var dom = c.el.dom;
2613         dom.parentNode.removeChild(dom);
2614         dom.style.width = ''; // clear with - which is set by drag.
2615         c.parentId = false;
2616         
2617     },
2618     
2619     /**    Decide whether to drop above or below a View node. */
2620     getDropPoint : function(e, n, dd)
2621     {
2622         if (dd) {
2623              return false;
2624         }
2625         if (n == this.containerEl.dom) {
2626             return "above";
2627         }
2628         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2629         var c = t + (b - t) / 2;
2630         var y = Roo.lib.Event.getPageY(e);
2631         if(y <= c) {
2632             return "above";
2633         }else{
2634             return "below";
2635         }
2636     },
2637     onToggleCollapse : function(e)
2638         {
2639         if (this.collapsed) {
2640             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2641             this.collapsableEl.addClass('show');
2642             this.collapsed = false;
2643             return;
2644         }
2645         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2646         this.collapsableEl.removeClass('show');
2647         this.collapsed = true;
2648         
2649     
2650     },
2651     
2652     onToggleRotate : function(e)
2653     {
2654         this.collapsableEl.removeClass('show');
2655         this.footerEl.removeClass('d-none');
2656         this.el.removeClass('roo-card-rotated');
2657         this.el.removeClass('d-none');
2658         if (this.rotated) {
2659             
2660             this.collapsableEl.addClass('show');
2661             this.rotated = false;
2662             this.fireEvent('rotate', this, this.rotated);
2663             return;
2664         }
2665         this.el.addClass('roo-card-rotated');
2666         this.footerEl.addClass('d-none');
2667         this.el.select('.roo-collapsable').removeClass('show');
2668         
2669         this.rotated = true;
2670         this.fireEvent('rotate', this, this.rotated);
2671     
2672     },
2673     
2674     dropPlaceHolder: function (action, info, data)
2675     {
2676         if (this.dropEl === false) {
2677             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2678             cls : 'd-none'
2679             },true);
2680         }
2681         this.dropEl.removeClass(['d-none', 'd-block']);        
2682         if (action == 'hide') {
2683             
2684             this.dropEl.addClass('d-none');
2685             return;
2686         }
2687         // FIXME - info.card == true!!!
2688         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2689         
2690         if (info.card !== true) {
2691             var cardel = info.card.el.dom;
2692             
2693             if (info.position == 'above') {
2694                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2695             } else if (cardel.nextSibling) {
2696                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2697             } else {
2698                 cardel.parentNode.append(this.dropEl.dom);
2699             }
2700         } else {
2701             // card container???
2702             this.containerEl.dom.append(this.dropEl.dom);
2703         }
2704         
2705         this.dropEl.addClass('d-block roo-card-dropzone');
2706         
2707         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2708         
2709         
2710     
2711     
2712     
2713     },
2714     setHeaderText: function(html)
2715     {
2716         this.header = html;
2717         if (this.headerContainerEl) {
2718             this.headerContainerEl.dom.innerHTML = html;
2719         }
2720     },
2721     onHeaderImageLoad : function(ev, he)
2722     {
2723         if (!this.header_image_fit_square) {
2724             return;
2725         }
2726         
2727         var hw = he.naturalHeight / he.naturalWidth;
2728         // wide image = < 0
2729         // tall image = > 1
2730         //var w = he.dom.naturalWidth;
2731         var ww = he.width;
2732         he.style.left =  0;
2733         he.style.position =  'relative';
2734         if (hw > 1) {
2735             var nw = (ww * (1/hw));
2736             Roo.get(he).setSize( ww * (1/hw),  ww);
2737             he.style.left =  ((ww - nw)/ 2) + 'px';
2738             he.style.position =  'relative';
2739         }
2740
2741     }
2742
2743     
2744 });
2745
2746 /*
2747  * - LGPL
2748  *
2749  * Card header - holder for the card header elements.
2750  * 
2751  */
2752
2753 /**
2754  * @class Roo.bootstrap.CardHeader
2755  * @extends Roo.bootstrap.Element
2756  * Bootstrap CardHeader class
2757  * @constructor
2758  * Create a new Card Header - that you can embed children into
2759  * @param {Object} config The config object
2760  */
2761
2762 Roo.bootstrap.CardHeader = function(config){
2763     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2764 };
2765
2766 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2767     
2768     
2769     container_method : 'getCardHeader' 
2770     
2771      
2772     
2773     
2774    
2775 });
2776
2777  
2778
2779  /*
2780  * - LGPL
2781  *
2782  * Card footer - holder for the card footer elements.
2783  * 
2784  */
2785
2786 /**
2787  * @class Roo.bootstrap.CardFooter
2788  * @extends Roo.bootstrap.Element
2789  * Bootstrap CardFooter class
2790  * @constructor
2791  * Create a new Card Footer - that you can embed children into
2792  * @param {Object} config The config object
2793  */
2794
2795 Roo.bootstrap.CardFooter = function(config){
2796     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2797 };
2798
2799 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2800     
2801     
2802     container_method : 'getCardFooter' 
2803     
2804      
2805     
2806     
2807    
2808 });
2809
2810  
2811
2812  /*
2813  * - LGPL
2814  *
2815  * Card header - holder for the card header elements.
2816  * 
2817  */
2818
2819 /**
2820  * @class Roo.bootstrap.CardImageTop
2821  * @extends Roo.bootstrap.Element
2822  * Bootstrap CardImageTop class
2823  * @constructor
2824  * Create a new Card Image Top container
2825  * @param {Object} config The config object
2826  */
2827
2828 Roo.bootstrap.CardImageTop = function(config){
2829     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2830 };
2831
2832 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2833     
2834    
2835     container_method : 'getCardImageTop' 
2836     
2837      
2838     
2839    
2840 });
2841
2842  
2843
2844  /*
2845  * - LGPL
2846  *
2847  * image
2848  * 
2849  */
2850
2851
2852 /**
2853  * @class Roo.bootstrap.Img
2854  * @extends Roo.bootstrap.Component
2855  * Bootstrap Img class
2856  * @cfg {Boolean} imgResponsive false | true
2857  * @cfg {String} border rounded | circle | thumbnail
2858  * @cfg {String} src image source
2859  * @cfg {String} alt image alternative text
2860  * @cfg {String} href a tag href
2861  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2862  * @cfg {String} xsUrl xs image source
2863  * @cfg {String} smUrl sm image source
2864  * @cfg {String} mdUrl md image source
2865  * @cfg {String} lgUrl lg image source
2866  * 
2867  * @constructor
2868  * Create a new Input
2869  * @param {Object} config The config object
2870  */
2871
2872 Roo.bootstrap.Img = function(config){
2873     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2874     
2875     this.addEvents({
2876         // img events
2877         /**
2878          * @event click
2879          * The img click event for the img.
2880          * @param {Roo.EventObject} e
2881          */
2882         "click" : true
2883     });
2884 };
2885
2886 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2887     
2888     imgResponsive: true,
2889     border: '',
2890     src: 'about:blank',
2891     href: false,
2892     target: false,
2893     xsUrl: '',
2894     smUrl: '',
2895     mdUrl: '',
2896     lgUrl: '',
2897
2898     getAutoCreate : function()
2899     {   
2900         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2901             return this.createSingleImg();
2902         }
2903         
2904         var cfg = {
2905             tag: 'div',
2906             cls: 'roo-image-responsive-group',
2907             cn: []
2908         };
2909         var _this = this;
2910         
2911         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2912             
2913             if(!_this[size + 'Url']){
2914                 return;
2915             }
2916             
2917             var img = {
2918                 tag: 'img',
2919                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2920                 html: _this.html || cfg.html,
2921                 src: _this[size + 'Url']
2922             };
2923             
2924             img.cls += ' roo-image-responsive-' + size;
2925             
2926             var s = ['xs', 'sm', 'md', 'lg'];
2927             
2928             s.splice(s.indexOf(size), 1);
2929             
2930             Roo.each(s, function(ss){
2931                 img.cls += ' hidden-' + ss;
2932             });
2933             
2934             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2935                 cfg.cls += ' img-' + _this.border;
2936             }
2937             
2938             if(_this.alt){
2939                 cfg.alt = _this.alt;
2940             }
2941             
2942             if(_this.href){
2943                 var a = {
2944                     tag: 'a',
2945                     href: _this.href,
2946                     cn: [
2947                         img
2948                     ]
2949                 };
2950
2951                 if(this.target){
2952                     a.target = _this.target;
2953                 }
2954             }
2955             
2956             cfg.cn.push((_this.href) ? a : img);
2957             
2958         });
2959         
2960         return cfg;
2961     },
2962     
2963     createSingleImg : function()
2964     {
2965         var cfg = {
2966             tag: 'img',
2967             cls: (this.imgResponsive) ? 'img-responsive' : '',
2968             html : null,
2969             src : 'about:blank'  // just incase src get's set to undefined?!?
2970         };
2971         
2972         cfg.html = this.html || cfg.html;
2973         
2974         cfg.src = this.src || cfg.src;
2975         
2976         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2977             cfg.cls += ' img-' + this.border;
2978         }
2979         
2980         if(this.alt){
2981             cfg.alt = this.alt;
2982         }
2983         
2984         if(this.href){
2985             var a = {
2986                 tag: 'a',
2987                 href: this.href,
2988                 cn: [
2989                     cfg
2990                 ]
2991             };
2992             
2993             if(this.target){
2994                 a.target = this.target;
2995             }
2996             
2997         }
2998         
2999         return (this.href) ? a : cfg;
3000     },
3001     
3002     initEvents: function() 
3003     {
3004         if(!this.href){
3005             this.el.on('click', this.onClick, this);
3006         }
3007         
3008     },
3009     
3010     onClick : function(e)
3011     {
3012         Roo.log('img onclick');
3013         this.fireEvent('click', this, e);
3014     },
3015     /**
3016      * Sets the url of the image - used to update it
3017      * @param {String} url the url of the image
3018      */
3019     
3020     setSrc : function(url)
3021     {
3022         this.src =  url;
3023         
3024         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3025             this.el.dom.src =  url;
3026             return;
3027         }
3028         
3029         this.el.select('img', true).first().dom.src =  url;
3030     }
3031     
3032     
3033    
3034 });
3035
3036  /*
3037  * - LGPL
3038  *
3039  * image
3040  * 
3041  */
3042
3043
3044 /**
3045  * @class Roo.bootstrap.Link
3046  * @extends Roo.bootstrap.Component
3047  * Bootstrap Link Class
3048  * @cfg {String} alt image alternative text
3049  * @cfg {String} href a tag href
3050  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3051  * @cfg {String} html the content of the link.
3052  * @cfg {String} anchor name for the anchor link
3053  * @cfg {String} fa - favicon
3054
3055  * @cfg {Boolean} preventDefault (true | false) default false
3056
3057  * 
3058  * @constructor
3059  * Create a new Input
3060  * @param {Object} config The config object
3061  */
3062
3063 Roo.bootstrap.Link = function(config){
3064     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3065     
3066     this.addEvents({
3067         // img events
3068         /**
3069          * @event click
3070          * The img click event for the img.
3071          * @param {Roo.EventObject} e
3072          */
3073         "click" : true
3074     });
3075 };
3076
3077 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3078     
3079     href: false,
3080     target: false,
3081     preventDefault: false,
3082     anchor : false,
3083     alt : false,
3084     fa: false,
3085
3086
3087     getAutoCreate : function()
3088     {
3089         var html = this.html || '';
3090         
3091         if (this.fa !== false) {
3092             html = '<i class="fa fa-' + this.fa + '"></i>';
3093         }
3094         var cfg = {
3095             tag: 'a'
3096         };
3097         // anchor's do not require html/href...
3098         if (this.anchor === false) {
3099             cfg.html = html;
3100             cfg.href = this.href || '#';
3101         } else {
3102             cfg.name = this.anchor;
3103             if (this.html !== false || this.fa !== false) {
3104                 cfg.html = html;
3105             }
3106             if (this.href !== false) {
3107                 cfg.href = this.href;
3108             }
3109         }
3110         
3111         if(this.alt !== false){
3112             cfg.alt = this.alt;
3113         }
3114         
3115         
3116         if(this.target !== false) {
3117             cfg.target = this.target;
3118         }
3119         
3120         return cfg;
3121     },
3122     
3123     initEvents: function() {
3124         
3125         if(!this.href || this.preventDefault){
3126             this.el.on('click', this.onClick, this);
3127         }
3128     },
3129     
3130     onClick : function(e)
3131     {
3132         if(this.preventDefault){
3133             e.preventDefault();
3134         }
3135         //Roo.log('img onclick');
3136         this.fireEvent('click', this, e);
3137     }
3138    
3139 });
3140
3141  /*
3142  * - LGPL
3143  *
3144  * header
3145  * 
3146  */
3147
3148 /**
3149  * @class Roo.bootstrap.Header
3150  * @extends Roo.bootstrap.Component
3151  * Bootstrap Header class
3152  * @cfg {String} html content of header
3153  * @cfg {Number} level (1|2|3|4|5|6) default 1
3154  * 
3155  * @constructor
3156  * Create a new Header
3157  * @param {Object} config The config object
3158  */
3159
3160
3161 Roo.bootstrap.Header  = function(config){
3162     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3163 };
3164
3165 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3166     
3167     //href : false,
3168     html : false,
3169     level : 1,
3170     
3171     
3172     
3173     getAutoCreate : function(){
3174         
3175         
3176         
3177         var cfg = {
3178             tag: 'h' + (1 *this.level),
3179             html: this.html || ''
3180         } ;
3181         
3182         return cfg;
3183     }
3184    
3185 });
3186
3187  
3188
3189  /*
3190  * Based on:
3191  * Ext JS Library 1.1.1
3192  * Copyright(c) 2006-2007, Ext JS, LLC.
3193  *
3194  * Originally Released Under LGPL - original licence link has changed is not relivant.
3195  *
3196  * Fork - LGPL
3197  * <script type="text/javascript">
3198  */
3199  
3200 /**
3201  * @class Roo.bootstrap.MenuMgr
3202  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3203  * @singleton
3204  */
3205 Roo.bootstrap.MenuMgr = function(){
3206    var menus, active, groups = {}, attached = false, lastShow = new Date();
3207
3208    // private - called when first menu is created
3209    function init(){
3210        menus = {};
3211        active = new Roo.util.MixedCollection();
3212        Roo.get(document).addKeyListener(27, function(){
3213            if(active.length > 0){
3214                hideAll();
3215            }
3216        });
3217    }
3218
3219    // private
3220    function hideAll(){
3221        if(active && active.length > 0){
3222            var c = active.clone();
3223            c.each(function(m){
3224                m.hide();
3225            });
3226        }
3227    }
3228
3229    // private
3230    function onHide(m){
3231        active.remove(m);
3232        if(active.length < 1){
3233            Roo.get(document).un("mouseup", onMouseDown);
3234             
3235            attached = false;
3236        }
3237    }
3238
3239    // private
3240    function onShow(m){
3241        var last = active.last();
3242        lastShow = new Date();
3243        active.add(m);
3244        if(!attached){
3245           Roo.get(document).on("mouseup", onMouseDown);
3246            
3247            attached = true;
3248        }
3249        if(m.parentMenu){
3250           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3251           m.parentMenu.activeChild = m;
3252        }else if(last && last.isVisible()){
3253           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3254        }
3255    }
3256
3257    // private
3258    function onBeforeHide(m){
3259        if(m.activeChild){
3260            m.activeChild.hide();
3261        }
3262        if(m.autoHideTimer){
3263            clearTimeout(m.autoHideTimer);
3264            delete m.autoHideTimer;
3265        }
3266    }
3267
3268    // private
3269    function onBeforeShow(m){
3270        var pm = m.parentMenu;
3271        if(!pm && !m.allowOtherMenus){
3272            hideAll();
3273        }else if(pm && pm.activeChild && active != m){
3274            pm.activeChild.hide();
3275        }
3276    }
3277
3278    // private this should really trigger on mouseup..
3279    function onMouseDown(e){
3280         Roo.log("on Mouse Up");
3281         
3282         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3283             Roo.log("MenuManager hideAll");
3284             hideAll();
3285             e.stopEvent();
3286         }
3287         
3288         
3289    }
3290
3291    // private
3292    function onBeforeCheck(mi, state){
3293        if(state){
3294            var g = groups[mi.group];
3295            for(var i = 0, l = g.length; i < l; i++){
3296                if(g[i] != mi){
3297                    g[i].setChecked(false);
3298                }
3299            }
3300        }
3301    }
3302
3303    return {
3304
3305        /**
3306         * Hides all menus that are currently visible
3307         */
3308        hideAll : function(){
3309             hideAll();  
3310        },
3311
3312        // private
3313        register : function(menu){
3314            if(!menus){
3315                init();
3316            }
3317            menus[menu.id] = menu;
3318            menu.on("beforehide", onBeforeHide);
3319            menu.on("hide", onHide);
3320            menu.on("beforeshow", onBeforeShow);
3321            menu.on("show", onShow);
3322            var g = menu.group;
3323            if(g && menu.events["checkchange"]){
3324                if(!groups[g]){
3325                    groups[g] = [];
3326                }
3327                groups[g].push(menu);
3328                menu.on("checkchange", onCheck);
3329            }
3330        },
3331
3332         /**
3333          * Returns a {@link Roo.menu.Menu} object
3334          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3335          * be used to generate and return a new Menu instance.
3336          */
3337        get : function(menu){
3338            if(typeof menu == "string"){ // menu id
3339                return menus[menu];
3340            }else if(menu.events){  // menu instance
3341                return menu;
3342            }
3343            /*else if(typeof menu.length == 'number'){ // array of menu items?
3344                return new Roo.bootstrap.Menu({items:menu});
3345            }else{ // otherwise, must be a config
3346                return new Roo.bootstrap.Menu(menu);
3347            }
3348            */
3349            return false;
3350        },
3351
3352        // private
3353        unregister : function(menu){
3354            delete menus[menu.id];
3355            menu.un("beforehide", onBeforeHide);
3356            menu.un("hide", onHide);
3357            menu.un("beforeshow", onBeforeShow);
3358            menu.un("show", onShow);
3359            var g = menu.group;
3360            if(g && menu.events["checkchange"]){
3361                groups[g].remove(menu);
3362                menu.un("checkchange", onCheck);
3363            }
3364        },
3365
3366        // private
3367        registerCheckable : function(menuItem){
3368            var g = menuItem.group;
3369            if(g){
3370                if(!groups[g]){
3371                    groups[g] = [];
3372                }
3373                groups[g].push(menuItem);
3374                menuItem.on("beforecheckchange", onBeforeCheck);
3375            }
3376        },
3377
3378        // private
3379        unregisterCheckable : function(menuItem){
3380            var g = menuItem.group;
3381            if(g){
3382                groups[g].remove(menuItem);
3383                menuItem.un("beforecheckchange", onBeforeCheck);
3384            }
3385        }
3386    };
3387 }();/*
3388  * - LGPL
3389  *
3390  * menu
3391  * 
3392  */
3393
3394 /**
3395  * @class Roo.bootstrap.Menu
3396  * @extends Roo.bootstrap.Component
3397  * Bootstrap Menu class - container for MenuItems
3398  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3399  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3400  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3401  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3402  * 
3403  * @constructor
3404  * Create a new Menu
3405  * @param {Object} config The config object
3406  */
3407
3408
3409 Roo.bootstrap.Menu = function(config){
3410     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3411     if (this.registerMenu && this.type != 'treeview')  {
3412         Roo.bootstrap.MenuMgr.register(this);
3413     }
3414     
3415     
3416     this.addEvents({
3417         /**
3418          * @event beforeshow
3419          * Fires before this menu is displayed (return false to block)
3420          * @param {Roo.menu.Menu} this
3421          */
3422         beforeshow : true,
3423         /**
3424          * @event beforehide
3425          * Fires before this menu is hidden (return false to block)
3426          * @param {Roo.menu.Menu} this
3427          */
3428         beforehide : true,
3429         /**
3430          * @event show
3431          * Fires after this menu is displayed
3432          * @param {Roo.menu.Menu} this
3433          */
3434         show : true,
3435         /**
3436          * @event hide
3437          * Fires after this menu is hidden
3438          * @param {Roo.menu.Menu} this
3439          */
3440         hide : true,
3441         /**
3442          * @event click
3443          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3444          * @param {Roo.menu.Menu} this
3445          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3446          * @param {Roo.EventObject} e
3447          */
3448         click : true,
3449         /**
3450          * @event mouseover
3451          * Fires when the mouse is hovering over this menu
3452          * @param {Roo.menu.Menu} this
3453          * @param {Roo.EventObject} e
3454          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3455          */
3456         mouseover : true,
3457         /**
3458          * @event mouseout
3459          * Fires when the mouse exits this menu
3460          * @param {Roo.menu.Menu} this
3461          * @param {Roo.EventObject} e
3462          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3463          */
3464         mouseout : true,
3465         /**
3466          * @event itemclick
3467          * Fires when a menu item contained in this menu is clicked
3468          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3469          * @param {Roo.EventObject} e
3470          */
3471         itemclick: true
3472     });
3473     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3474 };
3475
3476 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3477     
3478    /// html : false,
3479     //align : '',
3480     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3481     type: false,
3482     /**
3483      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3484      */
3485     registerMenu : true,
3486     
3487     menuItems :false, // stores the menu items..
3488     
3489     hidden:true,
3490         
3491     parentMenu : false,
3492     
3493     stopEvent : true,
3494     
3495     isLink : false,
3496     
3497     getChildContainer : function() {
3498         return this.el;  
3499     },
3500     
3501     getAutoCreate : function(){
3502          
3503         //if (['right'].indexOf(this.align)!==-1) {
3504         //    cfg.cn[1].cls += ' pull-right'
3505         //}
3506         
3507         
3508         var cfg = {
3509             tag : 'ul',
3510             cls : 'dropdown-menu' ,
3511             style : 'z-index:1000'
3512             
3513         };
3514         
3515         if (this.type === 'submenu') {
3516             cfg.cls = 'submenu active';
3517         }
3518         if (this.type === 'treeview') {
3519             cfg.cls = 'treeview-menu';
3520         }
3521         
3522         return cfg;
3523     },
3524     initEvents : function() {
3525         
3526        // Roo.log("ADD event");
3527        // Roo.log(this.triggerEl.dom);
3528         
3529         this.triggerEl.on('click', this.onTriggerClick, this);
3530         
3531         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3532         
3533         
3534         if (this.triggerEl.hasClass('nav-item')) {
3535             // dropdown toggle on the 'a' in BS4?
3536             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3537         } else {
3538             this.triggerEl.addClass('dropdown-toggle');
3539         }
3540         if (Roo.isTouch) {
3541             this.el.on('touchstart'  , this.onTouch, this);
3542         }
3543         this.el.on('click' , this.onClick, this);
3544
3545         this.el.on("mouseover", this.onMouseOver, this);
3546         this.el.on("mouseout", this.onMouseOut, this);
3547         
3548     },
3549     
3550     findTargetItem : function(e)
3551     {
3552         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3553         if(!t){
3554             return false;
3555         }
3556         //Roo.log(t);         Roo.log(t.id);
3557         if(t && t.id){
3558             //Roo.log(this.menuitems);
3559             return this.menuitems.get(t.id);
3560             
3561             //return this.items.get(t.menuItemId);
3562         }
3563         
3564         return false;
3565     },
3566     
3567     onTouch : function(e) 
3568     {
3569         Roo.log("menu.onTouch");
3570         //e.stopEvent(); this make the user popdown broken
3571         this.onClick(e);
3572     },
3573     
3574     onClick : function(e)
3575     {
3576         Roo.log("menu.onClick");
3577         
3578         var t = this.findTargetItem(e);
3579         if(!t || t.isContainer){
3580             return;
3581         }
3582         Roo.log(e);
3583         /*
3584         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3585             if(t == this.activeItem && t.shouldDeactivate(e)){
3586                 this.activeItem.deactivate();
3587                 delete this.activeItem;
3588                 return;
3589             }
3590             if(t.canActivate){
3591                 this.setActiveItem(t, true);
3592             }
3593             return;
3594             
3595             
3596         }
3597         */
3598        
3599         Roo.log('pass click event');
3600         
3601         t.onClick(e);
3602         
3603         this.fireEvent("click", this, t, e);
3604         
3605         var _this = this;
3606         
3607         if(!t.href.length || t.href == '#'){
3608             (function() { _this.hide(); }).defer(100);
3609         }
3610         
3611     },
3612     
3613     onMouseOver : function(e){
3614         var t  = this.findTargetItem(e);
3615         //Roo.log(t);
3616         //if(t){
3617         //    if(t.canActivate && !t.disabled){
3618         //        this.setActiveItem(t, true);
3619         //    }
3620         //}
3621         
3622         this.fireEvent("mouseover", this, e, t);
3623     },
3624     isVisible : function(){
3625         return !this.hidden;
3626     },
3627     onMouseOut : function(e){
3628         var t  = this.findTargetItem(e);
3629         
3630         //if(t ){
3631         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3632         //        this.activeItem.deactivate();
3633         //        delete this.activeItem;
3634         //    }
3635         //}
3636         this.fireEvent("mouseout", this, e, t);
3637     },
3638     
3639     
3640     /**
3641      * Displays this menu relative to another element
3642      * @param {String/HTMLElement/Roo.Element} element The element to align to
3643      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3644      * the element (defaults to this.defaultAlign)
3645      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3646      */
3647     show : function(el, pos, parentMenu)
3648     {
3649         if (false === this.fireEvent("beforeshow", this)) {
3650             Roo.log("show canceled");
3651             return;
3652         }
3653         this.parentMenu = parentMenu;
3654         if(!this.el){
3655             this.render();
3656         }
3657         
3658         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3659     },
3660      /**
3661      * Displays this menu at a specific xy position
3662      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3663      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3664      */
3665     showAt : function(xy, parentMenu, /* private: */_e){
3666         this.parentMenu = parentMenu;
3667         if(!this.el){
3668             this.render();
3669         }
3670         if(_e !== false){
3671             this.fireEvent("beforeshow", this);
3672             //xy = this.el.adjustForConstraints(xy);
3673         }
3674         
3675         //this.el.show();
3676         this.hideMenuItems();
3677         this.hidden = false;
3678         this.triggerEl.addClass('open');
3679         this.el.addClass('show');
3680         
3681         // reassign x when hitting right
3682         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3683             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3684         }
3685         
3686         // reassign y when hitting bottom
3687         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3688             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3689         }
3690         
3691         // but the list may align on trigger left or trigger top... should it be a properity?
3692         
3693         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3694             this.el.setXY(xy);
3695         }
3696         
3697         this.focus();
3698         this.fireEvent("show", this);
3699     },
3700     
3701     focus : function(){
3702         return;
3703         if(!this.hidden){
3704             this.doFocus.defer(50, this);
3705         }
3706     },
3707
3708     doFocus : function(){
3709         if(!this.hidden){
3710             this.focusEl.focus();
3711         }
3712     },
3713
3714     /**
3715      * Hides this menu and optionally all parent menus
3716      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3717      */
3718     hide : function(deep)
3719     {
3720         if (false === this.fireEvent("beforehide", this)) {
3721             Roo.log("hide canceled");
3722             return;
3723         }
3724         this.hideMenuItems();
3725         if(this.el && this.isVisible()){
3726            
3727             if(this.activeItem){
3728                 this.activeItem.deactivate();
3729                 this.activeItem = null;
3730             }
3731             this.triggerEl.removeClass('open');;
3732             this.el.removeClass('show');
3733             this.hidden = true;
3734             this.fireEvent("hide", this);
3735         }
3736         if(deep === true && this.parentMenu){
3737             this.parentMenu.hide(true);
3738         }
3739     },
3740     
3741     onTriggerClick : function(e)
3742     {
3743         Roo.log('trigger click');
3744         
3745         var target = e.getTarget();
3746         
3747         Roo.log(target.nodeName.toLowerCase());
3748         
3749         if(target.nodeName.toLowerCase() === 'i'){
3750             e.preventDefault();
3751         }
3752         
3753     },
3754     
3755     onTriggerPress  : function(e)
3756     {
3757         Roo.log('trigger press');
3758         //Roo.log(e.getTarget());
3759        // Roo.log(this.triggerEl.dom);
3760        
3761         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3762         var pel = Roo.get(e.getTarget());
3763         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3764             Roo.log('is treeview or dropdown?');
3765             return;
3766         }
3767         
3768         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3769             return;
3770         }
3771         
3772         if (this.isVisible()) {
3773             Roo.log('hide');
3774             this.hide();
3775         } else {
3776             Roo.log('show');
3777             this.show(this.triggerEl, '?', false);
3778         }
3779         
3780         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3781             e.stopEvent();
3782         }
3783         
3784     },
3785        
3786     
3787     hideMenuItems : function()
3788     {
3789         Roo.log("hide Menu Items");
3790         if (!this.el) { 
3791             return;
3792         }
3793         
3794         this.el.select('.open',true).each(function(aa) {
3795             
3796             aa.removeClass('open');
3797          
3798         });
3799     },
3800     addxtypeChild : function (tree, cntr) {
3801         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3802           
3803         this.menuitems.add(comp);
3804         return comp;
3805
3806     },
3807     getEl : function()
3808     {
3809         Roo.log(this.el);
3810         return this.el;
3811     },
3812     
3813     clear : function()
3814     {
3815         this.getEl().dom.innerHTML = '';
3816         this.menuitems.clear();
3817     }
3818 });
3819
3820  
3821  /*
3822  * - LGPL
3823  *
3824  * menu item
3825  * 
3826  */
3827
3828
3829 /**
3830  * @class Roo.bootstrap.MenuItem
3831  * @extends Roo.bootstrap.Component
3832  * Bootstrap MenuItem class
3833  * @cfg {String} html the menu label
3834  * @cfg {String} href the link
3835  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3836  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3837  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3838  * @cfg {String} fa favicon to show on left of menu item.
3839  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3840  * 
3841  * 
3842  * @constructor
3843  * Create a new MenuItem
3844  * @param {Object} config The config object
3845  */
3846
3847
3848 Roo.bootstrap.MenuItem = function(config){
3849     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3850     this.addEvents({
3851         // raw events
3852         /**
3853          * @event click
3854          * The raw click event for the entire grid.
3855          * @param {Roo.bootstrap.MenuItem} this
3856          * @param {Roo.EventObject} e
3857          */
3858         "click" : true
3859     });
3860 };
3861
3862 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3863     
3864     href : false,
3865     html : false,
3866     preventDefault: false,
3867     isContainer : false,
3868     active : false,
3869     fa: false,
3870     
3871     getAutoCreate : function(){
3872         
3873         if(this.isContainer){
3874             return {
3875                 tag: 'li',
3876                 cls: 'dropdown-menu-item '
3877             };
3878         }
3879         var ctag = {
3880             tag: 'span',
3881             html: 'Link'
3882         };
3883         
3884         var anc = {
3885             tag : 'a',
3886             cls : 'dropdown-item',
3887             href : '#',
3888             cn : [  ]
3889         };
3890         
3891         if (this.fa !== false) {
3892             anc.cn.push({
3893                 tag : 'i',
3894                 cls : 'fa fa-' + this.fa
3895             });
3896         }
3897         
3898         anc.cn.push(ctag);
3899         
3900         
3901         var cfg= {
3902             tag: 'li',
3903             cls: 'dropdown-menu-item',
3904             cn: [ anc ]
3905         };
3906         if (this.parent().type == 'treeview') {
3907             cfg.cls = 'treeview-menu';
3908         }
3909         if (this.active) {
3910             cfg.cls += ' active';
3911         }
3912         
3913         
3914         
3915         anc.href = this.href || cfg.cn[0].href ;
3916         ctag.html = this.html || cfg.cn[0].html ;
3917         return cfg;
3918     },
3919     
3920     initEvents: function()
3921     {
3922         if (this.parent().type == 'treeview') {
3923             this.el.select('a').on('click', this.onClick, this);
3924         }
3925         
3926         if (this.menu) {
3927             this.menu.parentType = this.xtype;
3928             this.menu.triggerEl = this.el;
3929             this.menu = this.addxtype(Roo.apply({}, this.menu));
3930         }
3931         
3932     },
3933     onClick : function(e)
3934     {
3935         Roo.log('item on click ');
3936         
3937         if(this.preventDefault){
3938             e.preventDefault();
3939         }
3940         //this.parent().hideMenuItems();
3941         
3942         this.fireEvent('click', this, e);
3943     },
3944     getEl : function()
3945     {
3946         return this.el;
3947     } 
3948 });
3949
3950  
3951
3952  /*
3953  * - LGPL
3954  *
3955  * menu separator
3956  * 
3957  */
3958
3959
3960 /**
3961  * @class Roo.bootstrap.MenuSeparator
3962  * @extends Roo.bootstrap.Component
3963  * Bootstrap MenuSeparator class
3964  * 
3965  * @constructor
3966  * Create a new MenuItem
3967  * @param {Object} config The config object
3968  */
3969
3970
3971 Roo.bootstrap.MenuSeparator = function(config){
3972     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3973 };
3974
3975 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3976     
3977     getAutoCreate : function(){
3978         var cfg = {
3979             cls: 'divider',
3980             tag : 'li'
3981         };
3982         
3983         return cfg;
3984     }
3985    
3986 });
3987
3988  
3989
3990  
3991 /*
3992 * Licence: LGPL
3993 */
3994
3995 /**
3996  * @class Roo.bootstrap.Modal
3997  * @extends Roo.bootstrap.Component
3998  * Bootstrap Modal class
3999  * @cfg {String} title Title of dialog
4000  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4001  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4002  * @cfg {Boolean} specificTitle default false
4003  * @cfg {Array} buttons Array of buttons or standard button set..
4004  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4005  * @cfg {Boolean} animate default true
4006  * @cfg {Boolean} allow_close default true
4007  * @cfg {Boolean} fitwindow default false
4008  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4009  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4010  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4011  * @cfg {String} size (sm|lg|xl) default empty
4012  * @cfg {Number} max_width set the max width of modal
4013  * @cfg {Boolean} editableTitle can the title be edited
4014
4015  *
4016  *
4017  * @constructor
4018  * Create a new Modal Dialog
4019  * @param {Object} config The config object
4020  */
4021
4022 Roo.bootstrap.Modal = function(config){
4023     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4024     this.addEvents({
4025         // raw events
4026         /**
4027          * @event btnclick
4028          * The raw btnclick event for the button
4029          * @param {Roo.EventObject} e
4030          */
4031         "btnclick" : true,
4032         /**
4033          * @event resize
4034          * Fire when dialog resize
4035          * @param {Roo.bootstrap.Modal} this
4036          * @param {Roo.EventObject} e
4037          */
4038         "resize" : true,
4039         /**
4040          * @event titlechanged
4041          * Fire when the editable title has been changed
4042          * @param {Roo.bootstrap.Modal} this
4043          * @param {Roo.EventObject} value
4044          */
4045         "titlechanged" : true 
4046         
4047     });
4048     this.buttons = this.buttons || [];
4049
4050     if (this.tmpl) {
4051         this.tmpl = Roo.factory(this.tmpl);
4052     }
4053
4054 };
4055
4056 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4057
4058     title : 'test dialog',
4059
4060     buttons : false,
4061
4062     // set on load...
4063
4064     html: false,
4065
4066     tmp: false,
4067
4068     specificTitle: false,
4069
4070     buttonPosition: 'right',
4071
4072     allow_close : true,
4073
4074     animate : true,
4075
4076     fitwindow: false,
4077     
4078      // private
4079     dialogEl: false,
4080     bodyEl:  false,
4081     footerEl:  false,
4082     titleEl:  false,
4083     closeEl:  false,
4084
4085     size: '',
4086     
4087     max_width: 0,
4088     
4089     max_height: 0,
4090     
4091     fit_content: false,
4092     editableTitle  : false,
4093
4094     onRender : function(ct, position)
4095     {
4096         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4097
4098         if(!this.el){
4099             var cfg = Roo.apply({},  this.getAutoCreate());
4100             cfg.id = Roo.id();
4101             //if(!cfg.name){
4102             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4103             //}
4104             //if (!cfg.name.length) {
4105             //    delete cfg.name;
4106            // }
4107             if (this.cls) {
4108                 cfg.cls += ' ' + this.cls;
4109             }
4110             if (this.style) {
4111                 cfg.style = this.style;
4112             }
4113             this.el = Roo.get(document.body).createChild(cfg, position);
4114         }
4115         //var type = this.el.dom.type;
4116
4117
4118         if(this.tabIndex !== undefined){
4119             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4120         }
4121
4122         this.dialogEl = this.el.select('.modal-dialog',true).first();
4123         this.bodyEl = this.el.select('.modal-body',true).first();
4124         this.closeEl = this.el.select('.modal-header .close', true).first();
4125         this.headerEl = this.el.select('.modal-header',true).first();
4126         this.titleEl = this.el.select('.modal-title',true).first();
4127         this.footerEl = this.el.select('.modal-footer',true).first();
4128
4129         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4130         
4131         //this.el.addClass("x-dlg-modal");
4132
4133         if (this.buttons.length) {
4134             Roo.each(this.buttons, function(bb) {
4135                 var b = Roo.apply({}, bb);
4136                 b.xns = b.xns || Roo.bootstrap;
4137                 b.xtype = b.xtype || 'Button';
4138                 if (typeof(b.listeners) == 'undefined') {
4139                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4140                 }
4141
4142                 var btn = Roo.factory(b);
4143
4144                 btn.render(this.getButtonContainer());
4145
4146             },this);
4147         }
4148         // render the children.
4149         var nitems = [];
4150
4151         if(typeof(this.items) != 'undefined'){
4152             var items = this.items;
4153             delete this.items;
4154
4155             for(var i =0;i < items.length;i++) {
4156                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4157             }
4158         }
4159
4160         this.items = nitems;
4161
4162         // where are these used - they used to be body/close/footer
4163
4164
4165         this.initEvents();
4166         //this.el.addClass([this.fieldClass, this.cls]);
4167
4168     },
4169
4170     getAutoCreate : function()
4171     {
4172         // we will default to modal-body-overflow - might need to remove or make optional later.
4173         var bdy = {
4174                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4175                 html : this.html || ''
4176         };
4177
4178         var title = {
4179             tag: 'h5',
4180             cls : 'modal-title',
4181             html : this.title
4182         };
4183
4184         if(this.specificTitle){ // WTF is this?
4185             title = this.title;
4186         }
4187
4188         var header = [];
4189         if (this.allow_close && Roo.bootstrap.version == 3) {
4190             header.push({
4191                 tag: 'button',
4192                 cls : 'close',
4193                 html : '&times'
4194             });
4195         }
4196
4197         header.push(title);
4198
4199         if (this.editableTitle) {
4200             header.push({
4201                 cls: 'form-control roo-editable-title d-none',
4202                 tag: 'input',
4203                 type: 'text'
4204             });
4205         }
4206         
4207         if (this.allow_close && Roo.bootstrap.version == 4) {
4208             header.push({
4209                 tag: 'button',
4210                 cls : 'close',
4211                 html : '&times'
4212             });
4213         }
4214         
4215         var size = '';
4216
4217         if(this.size.length){
4218             size = 'modal-' + this.size;
4219         }
4220         
4221         var footer = Roo.bootstrap.version == 3 ?
4222             {
4223                 cls : 'modal-footer',
4224                 cn : [
4225                     {
4226                         tag: 'div',
4227                         cls: 'btn-' + this.buttonPosition
4228                     }
4229                 ]
4230
4231             } :
4232             {  // BS4 uses mr-auto on left buttons....
4233                 cls : 'modal-footer'
4234             };
4235
4236             
4237
4238         
4239         
4240         var modal = {
4241             cls: "modal",
4242              cn : [
4243                 {
4244                     cls: "modal-dialog " + size,
4245                     cn : [
4246                         {
4247                             cls : "modal-content",
4248                             cn : [
4249                                 {
4250                                     cls : 'modal-header',
4251                                     cn : header
4252                                 },
4253                                 bdy,
4254                                 footer
4255                             ]
4256
4257                         }
4258                     ]
4259
4260                 }
4261             ]
4262         };
4263
4264         if(this.animate){
4265             modal.cls += ' fade';
4266         }
4267
4268         return modal;
4269
4270     },
4271     getChildContainer : function() {
4272
4273          return this.bodyEl;
4274
4275     },
4276     getButtonContainer : function() {
4277         
4278          return Roo.bootstrap.version == 4 ?
4279             this.el.select('.modal-footer',true).first()
4280             : this.el.select('.modal-footer div',true).first();
4281
4282     },
4283     initEvents : function()
4284     {
4285         if (this.allow_close) {
4286             this.closeEl.on('click', this.hide, this);
4287         }
4288         Roo.EventManager.onWindowResize(this.resize, this, true);
4289         if (this.editableTitle) {
4290             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4291             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4292             this.headerEditEl.on('keyup', function(e) {
4293                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4294                         this.toggleHeaderInput(false)
4295                     }
4296                 }, this);
4297             this.headerEditEl.on('blur', function(e) {
4298                 this.toggleHeaderInput(false)
4299             },this);
4300         }
4301
4302     },
4303   
4304
4305     resize : function()
4306     {
4307         this.maskEl.setSize(
4308             Roo.lib.Dom.getViewWidth(true),
4309             Roo.lib.Dom.getViewHeight(true)
4310         );
4311         
4312         if (this.fitwindow) {
4313             
4314            this.dialogEl.setStyle( { 'max-width' : '100%' });
4315             this.setSize(
4316                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4317                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4318             );
4319             return;
4320         }
4321         
4322         if(this.max_width !== 0) {
4323             
4324             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4325             
4326             if(this.height) {
4327                 this.setSize(w, this.height);
4328                 return;
4329             }
4330             
4331             if(this.max_height) {
4332                 this.setSize(w,Math.min(
4333                     this.max_height,
4334                     Roo.lib.Dom.getViewportHeight(true) - 60
4335                 ));
4336                 
4337                 return;
4338             }
4339             
4340             if(!this.fit_content) {
4341                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4342                 return;
4343             }
4344             
4345             this.setSize(w, Math.min(
4346                 60 +
4347                 this.headerEl.getHeight() + 
4348                 this.footerEl.getHeight() + 
4349                 this.getChildHeight(this.bodyEl.dom.childNodes),
4350                 Roo.lib.Dom.getViewportHeight(true) - 60)
4351             );
4352         }
4353         
4354     },
4355
4356     setSize : function(w,h)
4357     {
4358         if (!w && !h) {
4359             return;
4360         }
4361         
4362         this.resizeTo(w,h);
4363     },
4364
4365     show : function() {
4366
4367         if (!this.rendered) {
4368             this.render();
4369         }
4370         this.toggleHeaderInput(false);
4371         //this.el.setStyle('display', 'block');
4372         this.el.removeClass('hideing');
4373         this.el.dom.style.display='block';
4374         
4375         Roo.get(document.body).addClass('modal-open');
4376  
4377         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4378             
4379             (function(){
4380                 this.el.addClass('show');
4381                 this.el.addClass('in');
4382             }).defer(50, this);
4383         }else{
4384             this.el.addClass('show');
4385             this.el.addClass('in');
4386         }
4387
4388         // not sure how we can show data in here..
4389         //if (this.tmpl) {
4390         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4391         //}
4392
4393         Roo.get(document.body).addClass("x-body-masked");
4394         
4395         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4396         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4397         this.maskEl.dom.style.display = 'block';
4398         this.maskEl.addClass('show');
4399         
4400         
4401         this.resize();
4402         
4403         this.fireEvent('show', this);
4404
4405         // set zindex here - otherwise it appears to be ignored...
4406         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4407
4408         (function () {
4409             this.items.forEach( function(e) {
4410                 e.layout ? e.layout() : false;
4411
4412             });
4413         }).defer(100,this);
4414
4415     },
4416     hide : function()
4417     {
4418         if(this.fireEvent("beforehide", this) !== false){
4419             
4420             this.maskEl.removeClass('show');
4421             
4422             this.maskEl.dom.style.display = '';
4423             Roo.get(document.body).removeClass("x-body-masked");
4424             this.el.removeClass('in');
4425             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4426
4427             if(this.animate){ // why
4428                 this.el.addClass('hideing');
4429                 this.el.removeClass('show');
4430                 (function(){
4431                     if (!this.el.hasClass('hideing')) {
4432                         return; // it's been shown again...
4433                     }
4434                     
4435                     this.el.dom.style.display='';
4436
4437                     Roo.get(document.body).removeClass('modal-open');
4438                     this.el.removeClass('hideing');
4439                 }).defer(150,this);
4440                 
4441             }else{
4442                 this.el.removeClass('show');
4443                 this.el.dom.style.display='';
4444                 Roo.get(document.body).removeClass('modal-open');
4445
4446             }
4447             this.fireEvent('hide', this);
4448         }
4449     },
4450     isVisible : function()
4451     {
4452         
4453         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4454         
4455     },
4456
4457     addButton : function(str, cb)
4458     {
4459
4460
4461         var b = Roo.apply({}, { html : str } );
4462         b.xns = b.xns || Roo.bootstrap;
4463         b.xtype = b.xtype || 'Button';
4464         if (typeof(b.listeners) == 'undefined') {
4465             b.listeners = { click : cb.createDelegate(this)  };
4466         }
4467
4468         var btn = Roo.factory(b);
4469
4470         btn.render(this.getButtonContainer());
4471
4472         return btn;
4473
4474     },
4475
4476     setDefaultButton : function(btn)
4477     {
4478         //this.el.select('.modal-footer').()
4479     },
4480
4481     resizeTo: function(w,h)
4482     {
4483         this.dialogEl.setWidth(w);
4484         
4485         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4486
4487         this.bodyEl.setHeight(h - diff);
4488         
4489         this.fireEvent('resize', this);
4490     },
4491     
4492     setContentSize  : function(w, h)
4493     {
4494
4495     },
4496     onButtonClick: function(btn,e)
4497     {
4498         //Roo.log([a,b,c]);
4499         this.fireEvent('btnclick', btn.name, e);
4500     },
4501      /**
4502      * Set the title of the Dialog
4503      * @param {String} str new Title
4504      */
4505     setTitle: function(str) {
4506         this.titleEl.dom.innerHTML = str;
4507         this.title = str;
4508     },
4509     /**
4510      * Set the body of the Dialog
4511      * @param {String} str new Title
4512      */
4513     setBody: function(str) {
4514         this.bodyEl.dom.innerHTML = str;
4515     },
4516     /**
4517      * Set the body of the Dialog using the template
4518      * @param {Obj} data - apply this data to the template and replace the body contents.
4519      */
4520     applyBody: function(obj)
4521     {
4522         if (!this.tmpl) {
4523             Roo.log("Error - using apply Body without a template");
4524             //code
4525         }
4526         this.tmpl.overwrite(this.bodyEl, obj);
4527     },
4528     
4529     getChildHeight : function(child_nodes)
4530     {
4531         if(
4532             !child_nodes ||
4533             child_nodes.length == 0
4534         ) {
4535             return 0;
4536         }
4537         
4538         var child_height = 0;
4539         
4540         for(var i = 0; i < child_nodes.length; i++) {
4541             
4542             /*
4543             * for modal with tabs...
4544             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4545                 
4546                 var layout_childs = child_nodes[i].childNodes;
4547                 
4548                 for(var j = 0; j < layout_childs.length; j++) {
4549                     
4550                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4551                         
4552                         var layout_body_childs = layout_childs[j].childNodes;
4553                         
4554                         for(var k = 0; k < layout_body_childs.length; k++) {
4555                             
4556                             if(layout_body_childs[k].classList.contains('navbar')) {
4557                                 child_height += layout_body_childs[k].offsetHeight;
4558                                 continue;
4559                             }
4560                             
4561                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4562                                 
4563                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4564                                 
4565                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4566                                     
4567                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4568                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4569                                         continue;
4570                                     }
4571                                     
4572                                 }
4573                                 
4574                             }
4575                             
4576                         }
4577                     }
4578                 }
4579                 continue;
4580             }
4581             */
4582             
4583             child_height += child_nodes[i].offsetHeight;
4584             // Roo.log(child_nodes[i].offsetHeight);
4585         }
4586         
4587         return child_height;
4588     },
4589     toggleHeaderInput : function(is_edit)
4590     {
4591         if (!this.editableTitle) {
4592             return; // not editable.
4593         }
4594         if (is_edit && this.is_header_editing) {
4595             return; // already editing..
4596         }
4597         if (is_edit) {
4598     
4599             this.headerEditEl.dom.value = this.title;
4600             this.headerEditEl.removeClass('d-none');
4601             this.headerEditEl.dom.focus();
4602             this.titleEl.addClass('d-none');
4603             
4604             this.is_header_editing = true;
4605             return
4606         }
4607         // flip back to not editing.
4608         this.title = this.headerEditEl.dom.value;
4609         this.headerEditEl.addClass('d-none');
4610         this.titleEl.removeClass('d-none');
4611         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4612         this.is_header_editing = false;
4613         this.fireEvent('titlechanged', this, this.title);
4614     
4615             
4616         
4617     }
4618
4619 });
4620
4621
4622 Roo.apply(Roo.bootstrap.Modal,  {
4623     /**
4624          * Button config that displays a single OK button
4625          * @type Object
4626          */
4627         OK :  [{
4628             name : 'ok',
4629             weight : 'primary',
4630             html : 'OK'
4631         }],
4632         /**
4633          * Button config that displays Yes and No buttons
4634          * @type Object
4635          */
4636         YESNO : [
4637             {
4638                 name  : 'no',
4639                 html : 'No'
4640             },
4641             {
4642                 name  :'yes',
4643                 weight : 'primary',
4644                 html : 'Yes'
4645             }
4646         ],
4647
4648         /**
4649          * Button config that displays OK and Cancel buttons
4650          * @type Object
4651          */
4652         OKCANCEL : [
4653             {
4654                name : 'cancel',
4655                 html : 'Cancel'
4656             },
4657             {
4658                 name : 'ok',
4659                 weight : 'primary',
4660                 html : 'OK'
4661             }
4662         ],
4663         /**
4664          * Button config that displays Yes, No and Cancel buttons
4665          * @type Object
4666          */
4667         YESNOCANCEL : [
4668             {
4669                 name : 'yes',
4670                 weight : 'primary',
4671                 html : 'Yes'
4672             },
4673             {
4674                 name : 'no',
4675                 html : 'No'
4676             },
4677             {
4678                 name : 'cancel',
4679                 html : 'Cancel'
4680             }
4681         ],
4682         
4683         zIndex : 10001
4684 });
4685
4686 /*
4687  * - LGPL
4688  *
4689  * messagebox - can be used as a replace
4690  * 
4691  */
4692 /**
4693  * @class Roo.MessageBox
4694  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4695  * Example usage:
4696  *<pre><code>
4697 // Basic alert:
4698 Roo.Msg.alert('Status', 'Changes saved successfully.');
4699
4700 // Prompt for user data:
4701 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4702     if (btn == 'ok'){
4703         // process text value...
4704     }
4705 });
4706
4707 // Show a dialog using config options:
4708 Roo.Msg.show({
4709    title:'Save Changes?',
4710    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4711    buttons: Roo.Msg.YESNOCANCEL,
4712    fn: processResult,
4713    animEl: 'elId'
4714 });
4715 </code></pre>
4716  * @singleton
4717  */
4718 Roo.bootstrap.MessageBox = function(){
4719     var dlg, opt, mask, waitTimer;
4720     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4721     var buttons, activeTextEl, bwidth;
4722
4723     
4724     // private
4725     var handleButton = function(button){
4726         dlg.hide();
4727         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4728     };
4729
4730     // private
4731     var handleHide = function(){
4732         if(opt && opt.cls){
4733             dlg.el.removeClass(opt.cls);
4734         }
4735         //if(waitTimer){
4736         //    Roo.TaskMgr.stop(waitTimer);
4737         //    waitTimer = null;
4738         //}
4739     };
4740
4741     // private
4742     var updateButtons = function(b){
4743         var width = 0;
4744         if(!b){
4745             buttons["ok"].hide();
4746             buttons["cancel"].hide();
4747             buttons["yes"].hide();
4748             buttons["no"].hide();
4749             dlg.footerEl.hide();
4750             
4751             return width;
4752         }
4753         dlg.footerEl.show();
4754         for(var k in buttons){
4755             if(typeof buttons[k] != "function"){
4756                 if(b[k]){
4757                     buttons[k].show();
4758                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4759                     width += buttons[k].el.getWidth()+15;
4760                 }else{
4761                     buttons[k].hide();
4762                 }
4763             }
4764         }
4765         return width;
4766     };
4767
4768     // private
4769     var handleEsc = function(d, k, e){
4770         if(opt && opt.closable !== false){
4771             dlg.hide();
4772         }
4773         if(e){
4774             e.stopEvent();
4775         }
4776     };
4777
4778     return {
4779         /**
4780          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4781          * @return {Roo.BasicDialog} The BasicDialog element
4782          */
4783         getDialog : function(){
4784            if(!dlg){
4785                 dlg = new Roo.bootstrap.Modal( {
4786                     //draggable: true,
4787                     //resizable:false,
4788                     //constraintoviewport:false,
4789                     //fixedcenter:true,
4790                     //collapsible : false,
4791                     //shim:true,
4792                     //modal: true,
4793                 //    width: 'auto',
4794                   //  height:100,
4795                     //buttonAlign:"center",
4796                     closeClick : function(){
4797                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4798                             handleButton("no");
4799                         }else{
4800                             handleButton("cancel");
4801                         }
4802                     }
4803                 });
4804                 dlg.render();
4805                 dlg.on("hide", handleHide);
4806                 mask = dlg.mask;
4807                 //dlg.addKeyListener(27, handleEsc);
4808                 buttons = {};
4809                 this.buttons = buttons;
4810                 var bt = this.buttonText;
4811                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4812                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4813                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4814                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4815                 //Roo.log(buttons);
4816                 bodyEl = dlg.bodyEl.createChild({
4817
4818                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4819                         '<textarea class="roo-mb-textarea"></textarea>' +
4820                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4821                 });
4822                 msgEl = bodyEl.dom.firstChild;
4823                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4824                 textboxEl.enableDisplayMode();
4825                 textboxEl.addKeyListener([10,13], function(){
4826                     if(dlg.isVisible() && opt && opt.buttons){
4827                         if(opt.buttons.ok){
4828                             handleButton("ok");
4829                         }else if(opt.buttons.yes){
4830                             handleButton("yes");
4831                         }
4832                     }
4833                 });
4834                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4835                 textareaEl.enableDisplayMode();
4836                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4837                 progressEl.enableDisplayMode();
4838                 
4839                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4840                 var pf = progressEl.dom.firstChild;
4841                 if (pf) {
4842                     pp = Roo.get(pf.firstChild);
4843                     pp.setHeight(pf.offsetHeight);
4844                 }
4845                 
4846             }
4847             return dlg;
4848         },
4849
4850         /**
4851          * Updates the message box body text
4852          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4853          * the XHTML-compliant non-breaking space character '&amp;#160;')
4854          * @return {Roo.MessageBox} This message box
4855          */
4856         updateText : function(text)
4857         {
4858             if(!dlg.isVisible() && !opt.width){
4859                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4860                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4861             }
4862             msgEl.innerHTML = text || '&#160;';
4863       
4864             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4865             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4866             var w = Math.max(
4867                     Math.min(opt.width || cw , this.maxWidth), 
4868                     Math.max(opt.minWidth || this.minWidth, bwidth)
4869             );
4870             if(opt.prompt){
4871                 activeTextEl.setWidth(w);
4872             }
4873             if(dlg.isVisible()){
4874                 dlg.fixedcenter = false;
4875             }
4876             // to big, make it scroll. = But as usual stupid IE does not support
4877             // !important..
4878             
4879             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4880                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4881                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4882             } else {
4883                 bodyEl.dom.style.height = '';
4884                 bodyEl.dom.style.overflowY = '';
4885             }
4886             if (cw > w) {
4887                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4888             } else {
4889                 bodyEl.dom.style.overflowX = '';
4890             }
4891             
4892             dlg.setContentSize(w, bodyEl.getHeight());
4893             if(dlg.isVisible()){
4894                 dlg.fixedcenter = true;
4895             }
4896             return this;
4897         },
4898
4899         /**
4900          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4901          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4902          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4903          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4904          * @return {Roo.MessageBox} This message box
4905          */
4906         updateProgress : function(value, text){
4907             if(text){
4908                 this.updateText(text);
4909             }
4910             
4911             if (pp) { // weird bug on my firefox - for some reason this is not defined
4912                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4913                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4914             }
4915             return this;
4916         },        
4917
4918         /**
4919          * Returns true if the message box is currently displayed
4920          * @return {Boolean} True if the message box is visible, else false
4921          */
4922         isVisible : function(){
4923             return dlg && dlg.isVisible();  
4924         },
4925
4926         /**
4927          * Hides the message box if it is displayed
4928          */
4929         hide : function(){
4930             if(this.isVisible()){
4931                 dlg.hide();
4932             }  
4933         },
4934
4935         /**
4936          * Displays a new message box, or reinitializes an existing message box, based on the config options
4937          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4938          * The following config object properties are supported:
4939          * <pre>
4940 Property    Type             Description
4941 ----------  ---------------  ------------------------------------------------------------------------------------
4942 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4943                                    closes (defaults to undefined)
4944 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4945                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4946 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4947                                    progress and wait dialogs will ignore this property and always hide the
4948                                    close button as they can only be closed programmatically.
4949 cls               String           A custom CSS class to apply to the message box element
4950 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4951                                    displayed (defaults to 75)
4952 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4953                                    function will be btn (the name of the button that was clicked, if applicable,
4954                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4955                                    Progress and wait dialogs will ignore this option since they do not respond to
4956                                    user actions and can only be closed programmatically, so any required function
4957                                    should be called by the same code after it closes the dialog.
4958 icon              String           A CSS class that provides a background image to be used as an icon for
4959                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4960 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4961 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4962 modal             Boolean          False to allow user interaction with the page while the message box is
4963                                    displayed (defaults to true)
4964 msg               String           A string that will replace the existing message box body text (defaults
4965                                    to the XHTML-compliant non-breaking space character '&#160;')
4966 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4967 progress          Boolean          True to display a progress bar (defaults to false)
4968 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4969 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4970 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4971 title             String           The title text
4972 value             String           The string value to set into the active textbox element if displayed
4973 wait              Boolean          True to display a progress bar (defaults to false)
4974 width             Number           The width of the dialog in pixels
4975 </pre>
4976          *
4977          * Example usage:
4978          * <pre><code>
4979 Roo.Msg.show({
4980    title: 'Address',
4981    msg: 'Please enter your address:',
4982    width: 300,
4983    buttons: Roo.MessageBox.OKCANCEL,
4984    multiline: true,
4985    fn: saveAddress,
4986    animEl: 'addAddressBtn'
4987 });
4988 </code></pre>
4989          * @param {Object} config Configuration options
4990          * @return {Roo.MessageBox} This message box
4991          */
4992         show : function(options)
4993         {
4994             
4995             // this causes nightmares if you show one dialog after another
4996             // especially on callbacks..
4997              
4998             if(this.isVisible()){
4999                 
5000                 this.hide();
5001                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5002                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5003                 Roo.log("New Dialog Message:" +  options.msg )
5004                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5005                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5006                 
5007             }
5008             var d = this.getDialog();
5009             opt = options;
5010             d.setTitle(opt.title || "&#160;");
5011             d.closeEl.setDisplayed(opt.closable !== false);
5012             activeTextEl = textboxEl;
5013             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5014             if(opt.prompt){
5015                 if(opt.multiline){
5016                     textboxEl.hide();
5017                     textareaEl.show();
5018                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5019                         opt.multiline : this.defaultTextHeight);
5020                     activeTextEl = textareaEl;
5021                 }else{
5022                     textboxEl.show();
5023                     textareaEl.hide();
5024                 }
5025             }else{
5026                 textboxEl.hide();
5027                 textareaEl.hide();
5028             }
5029             progressEl.setDisplayed(opt.progress === true);
5030             if (opt.progress) {
5031                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5032             }
5033             this.updateProgress(0);
5034             activeTextEl.dom.value = opt.value || "";
5035             if(opt.prompt){
5036                 dlg.setDefaultButton(activeTextEl);
5037             }else{
5038                 var bs = opt.buttons;
5039                 var db = null;
5040                 if(bs && bs.ok){
5041                     db = buttons["ok"];
5042                 }else if(bs && bs.yes){
5043                     db = buttons["yes"];
5044                 }
5045                 dlg.setDefaultButton(db);
5046             }
5047             bwidth = updateButtons(opt.buttons);
5048             this.updateText(opt.msg);
5049             if(opt.cls){
5050                 d.el.addClass(opt.cls);
5051             }
5052             d.proxyDrag = opt.proxyDrag === true;
5053             d.modal = opt.modal !== false;
5054             d.mask = opt.modal !== false ? mask : false;
5055             if(!d.isVisible()){
5056                 // force it to the end of the z-index stack so it gets a cursor in FF
5057                 document.body.appendChild(dlg.el.dom);
5058                 d.animateTarget = null;
5059                 d.show(options.animEl);
5060             }
5061             return this;
5062         },
5063
5064         /**
5065          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5066          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5067          * and closing the message box when the process is complete.
5068          * @param {String} title The title bar text
5069          * @param {String} msg The message box body text
5070          * @return {Roo.MessageBox} This message box
5071          */
5072         progress : function(title, msg){
5073             this.show({
5074                 title : title,
5075                 msg : msg,
5076                 buttons: false,
5077                 progress:true,
5078                 closable:false,
5079                 minWidth: this.minProgressWidth,
5080                 modal : true
5081             });
5082             return this;
5083         },
5084
5085         /**
5086          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5087          * If a callback function is passed it will be called after the user clicks the button, and the
5088          * id of the button that was clicked will be passed as the only parameter to the callback
5089          * (could also be the top-right close button).
5090          * @param {String} title The title bar text
5091          * @param {String} msg The message box body text
5092          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5093          * @param {Object} scope (optional) The scope of the callback function
5094          * @return {Roo.MessageBox} This message box
5095          */
5096         alert : function(title, msg, fn, scope)
5097         {
5098             this.show({
5099                 title : title,
5100                 msg : msg,
5101                 buttons: this.OK,
5102                 fn: fn,
5103                 closable : false,
5104                 scope : scope,
5105                 modal : true
5106             });
5107             return this;
5108         },
5109
5110         /**
5111          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5112          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5113          * You are responsible for closing the message box when the process is complete.
5114          * @param {String} msg The message box body text
5115          * @param {String} title (optional) The title bar text
5116          * @return {Roo.MessageBox} This message box
5117          */
5118         wait : function(msg, title){
5119             this.show({
5120                 title : title,
5121                 msg : msg,
5122                 buttons: false,
5123                 closable:false,
5124                 progress:true,
5125                 modal:true,
5126                 width:300,
5127                 wait:true
5128             });
5129             waitTimer = Roo.TaskMgr.start({
5130                 run: function(i){
5131                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5132                 },
5133                 interval: 1000
5134             });
5135             return this;
5136         },
5137
5138         /**
5139          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5140          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5141          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5142          * @param {String} title The title bar text
5143          * @param {String} msg The message box body text
5144          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5145          * @param {Object} scope (optional) The scope of the callback function
5146          * @return {Roo.MessageBox} This message box
5147          */
5148         confirm : function(title, msg, fn, scope){
5149             this.show({
5150                 title : title,
5151                 msg : msg,
5152                 buttons: this.YESNO,
5153                 fn: fn,
5154                 scope : scope,
5155                 modal : true
5156             });
5157             return this;
5158         },
5159
5160         /**
5161          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5162          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5163          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5164          * (could also be the top-right close button) and the text that was entered will be passed as the two
5165          * parameters to the callback.
5166          * @param {String} title The title bar text
5167          * @param {String} msg The message box body text
5168          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5169          * @param {Object} scope (optional) The scope of the callback function
5170          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5171          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5172          * @return {Roo.MessageBox} This message box
5173          */
5174         prompt : function(title, msg, fn, scope, multiline){
5175             this.show({
5176                 title : title,
5177                 msg : msg,
5178                 buttons: this.OKCANCEL,
5179                 fn: fn,
5180                 minWidth:250,
5181                 scope : scope,
5182                 prompt:true,
5183                 multiline: multiline,
5184                 modal : true
5185             });
5186             return this;
5187         },
5188
5189         /**
5190          * Button config that displays a single OK button
5191          * @type Object
5192          */
5193         OK : {ok:true},
5194         /**
5195          * Button config that displays Yes and No buttons
5196          * @type Object
5197          */
5198         YESNO : {yes:true, no:true},
5199         /**
5200          * Button config that displays OK and Cancel buttons
5201          * @type Object
5202          */
5203         OKCANCEL : {ok:true, cancel:true},
5204         /**
5205          * Button config that displays Yes, No and Cancel buttons
5206          * @type Object
5207          */
5208         YESNOCANCEL : {yes:true, no:true, cancel:true},
5209
5210         /**
5211          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5212          * @type Number
5213          */
5214         defaultTextHeight : 75,
5215         /**
5216          * The maximum width in pixels of the message box (defaults to 600)
5217          * @type Number
5218          */
5219         maxWidth : 600,
5220         /**
5221          * The minimum width in pixels of the message box (defaults to 100)
5222          * @type Number
5223          */
5224         minWidth : 100,
5225         /**
5226          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5227          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5228          * @type Number
5229          */
5230         minProgressWidth : 250,
5231         /**
5232          * An object containing the default button text strings that can be overriden for localized language support.
5233          * Supported properties are: ok, cancel, yes and no.
5234          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5235          * @type Object
5236          */
5237         buttonText : {
5238             ok : "OK",
5239             cancel : "Cancel",
5240             yes : "Yes",
5241             no : "No"
5242         }
5243     };
5244 }();
5245
5246 /**
5247  * Shorthand for {@link Roo.MessageBox}
5248  */
5249 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5250 Roo.Msg = Roo.Msg || Roo.MessageBox;
5251 /*
5252  * - LGPL
5253  *
5254  * navbar
5255  * 
5256  */
5257
5258 /**
5259  * @class Roo.bootstrap.Navbar
5260  * @extends Roo.bootstrap.Component
5261  * Bootstrap Navbar class
5262
5263  * @constructor
5264  * Create a new Navbar
5265  * @param {Object} config The config object
5266  */
5267
5268
5269 Roo.bootstrap.Navbar = function(config){
5270     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5271     this.addEvents({
5272         // raw events
5273         /**
5274          * @event beforetoggle
5275          * Fire before toggle the menu
5276          * @param {Roo.EventObject} e
5277          */
5278         "beforetoggle" : true
5279     });
5280 };
5281
5282 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5283     
5284     
5285    
5286     // private
5287     navItems : false,
5288     loadMask : false,
5289     
5290     
5291     getAutoCreate : function(){
5292         
5293         
5294         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5295         
5296     },
5297     
5298     initEvents :function ()
5299     {
5300         //Roo.log(this.el.select('.navbar-toggle',true));
5301         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5302         
5303         var mark = {
5304             tag: "div",
5305             cls:"x-dlg-mask"
5306         };
5307         
5308         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5309         
5310         var size = this.el.getSize();
5311         this.maskEl.setSize(size.width, size.height);
5312         this.maskEl.enableDisplayMode("block");
5313         this.maskEl.hide();
5314         
5315         if(this.loadMask){
5316             this.maskEl.show();
5317         }
5318     },
5319     
5320     
5321     getChildContainer : function()
5322     {
5323         if (this.el && this.el.select('.collapse').getCount()) {
5324             return this.el.select('.collapse',true).first();
5325         }
5326         
5327         return this.el;
5328     },
5329     
5330     mask : function()
5331     {
5332         this.maskEl.show();
5333     },
5334     
5335     unmask : function()
5336     {
5337         this.maskEl.hide();
5338     },
5339     onToggle : function()
5340     {
5341         
5342         if(this.fireEvent('beforetoggle', this) === false){
5343             return;
5344         }
5345         var ce = this.el.select('.navbar-collapse',true).first();
5346       
5347         if (!ce.hasClass('show')) {
5348            this.expand();
5349         } else {
5350             this.collapse();
5351         }
5352         
5353         
5354     
5355     },
5356     /**
5357      * Expand the navbar pulldown 
5358      */
5359     expand : function ()
5360     {
5361        
5362         var ce = this.el.select('.navbar-collapse',true).first();
5363         if (ce.hasClass('collapsing')) {
5364             return;
5365         }
5366         ce.dom.style.height = '';
5367                // show it...
5368         ce.addClass('in'); // old...
5369         ce.removeClass('collapse');
5370         ce.addClass('show');
5371         var h = ce.getHeight();
5372         Roo.log(h);
5373         ce.removeClass('show');
5374         // at this point we should be able to see it..
5375         ce.addClass('collapsing');
5376         
5377         ce.setHeight(0); // resize it ...
5378         ce.on('transitionend', function() {
5379             //Roo.log('done transition');
5380             ce.removeClass('collapsing');
5381             ce.addClass('show');
5382             ce.removeClass('collapse');
5383
5384             ce.dom.style.height = '';
5385         }, this, { single: true} );
5386         ce.setHeight(h);
5387         ce.dom.scrollTop = 0;
5388     },
5389     /**
5390      * Collapse the navbar pulldown 
5391      */
5392     collapse : function()
5393     {
5394          var ce = this.el.select('.navbar-collapse',true).first();
5395        
5396         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5397             // it's collapsed or collapsing..
5398             return;
5399         }
5400         ce.removeClass('in'); // old...
5401         ce.setHeight(ce.getHeight());
5402         ce.removeClass('show');
5403         ce.addClass('collapsing');
5404         
5405         ce.on('transitionend', function() {
5406             ce.dom.style.height = '';
5407             ce.removeClass('collapsing');
5408             ce.addClass('collapse');
5409         }, this, { single: true} );
5410         ce.setHeight(0);
5411     }
5412     
5413     
5414     
5415 });
5416
5417
5418
5419  
5420
5421  /*
5422  * - LGPL
5423  *
5424  * navbar
5425  * 
5426  */
5427
5428 /**
5429  * @class Roo.bootstrap.NavSimplebar
5430  * @extends Roo.bootstrap.Navbar
5431  * Bootstrap Sidebar class
5432  *
5433  * @cfg {Boolean} inverse is inverted color
5434  * 
5435  * @cfg {String} type (nav | pills | tabs)
5436  * @cfg {Boolean} arrangement stacked | justified
5437  * @cfg {String} align (left | right) alignment
5438  * 
5439  * @cfg {Boolean} main (true|false) main nav bar? default false
5440  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5441  * 
5442  * @cfg {String} tag (header|footer|nav|div) default is nav 
5443
5444  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5445  * 
5446  * 
5447  * @constructor
5448  * Create a new Sidebar
5449  * @param {Object} config The config object
5450  */
5451
5452
5453 Roo.bootstrap.NavSimplebar = function(config){
5454     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5455 };
5456
5457 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5458     
5459     inverse: false,
5460     
5461     type: false,
5462     arrangement: '',
5463     align : false,
5464     
5465     weight : 'light',
5466     
5467     main : false,
5468     
5469     
5470     tag : false,
5471     
5472     
5473     getAutoCreate : function(){
5474         
5475         
5476         var cfg = {
5477             tag : this.tag || 'div',
5478             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5479         };
5480         if (['light','white'].indexOf(this.weight) > -1) {
5481             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5482         }
5483         cfg.cls += ' bg-' + this.weight;
5484         
5485         if (this.inverse) {
5486             cfg.cls += ' navbar-inverse';
5487             
5488         }
5489         
5490         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5491         
5492         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5493             return cfg;
5494         }
5495         
5496         
5497     
5498         
5499         cfg.cn = [
5500             {
5501                 cls: 'nav nav-' + this.xtype,
5502                 tag : 'ul'
5503             }
5504         ];
5505         
5506          
5507         this.type = this.type || 'nav';
5508         if (['tabs','pills'].indexOf(this.type) != -1) {
5509             cfg.cn[0].cls += ' nav-' + this.type
5510         
5511         
5512         } else {
5513             if (this.type!=='nav') {
5514                 Roo.log('nav type must be nav/tabs/pills')
5515             }
5516             cfg.cn[0].cls += ' navbar-nav'
5517         }
5518         
5519         
5520         
5521         
5522         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5523             cfg.cn[0].cls += ' nav-' + this.arrangement;
5524         }
5525         
5526         
5527         if (this.align === 'right') {
5528             cfg.cn[0].cls += ' navbar-right';
5529         }
5530         
5531         
5532         
5533         
5534         return cfg;
5535     
5536         
5537     }
5538     
5539     
5540     
5541 });
5542
5543
5544
5545  
5546
5547  
5548        /*
5549  * - LGPL
5550  *
5551  * navbar
5552  * navbar-fixed-top
5553  * navbar-expand-md  fixed-top 
5554  */
5555
5556 /**
5557  * @class Roo.bootstrap.NavHeaderbar
5558  * @extends Roo.bootstrap.NavSimplebar
5559  * Bootstrap Sidebar class
5560  *
5561  * @cfg {String} brand what is brand
5562  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5563  * @cfg {String} brand_href href of the brand
5564  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5565  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5566  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5567  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5568  * 
5569  * @constructor
5570  * Create a new Sidebar
5571  * @param {Object} config The config object
5572  */
5573
5574
5575 Roo.bootstrap.NavHeaderbar = function(config){
5576     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5577       
5578 };
5579
5580 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5581     
5582     position: '',
5583     brand: '',
5584     brand_href: false,
5585     srButton : true,
5586     autohide : false,
5587     desktopCenter : false,
5588    
5589     
5590     getAutoCreate : function(){
5591         
5592         var   cfg = {
5593             tag: this.nav || 'nav',
5594             cls: 'navbar navbar-expand-md',
5595             role: 'navigation',
5596             cn: []
5597         };
5598         
5599         var cn = cfg.cn;
5600         if (this.desktopCenter) {
5601             cn.push({cls : 'container', cn : []});
5602             cn = cn[0].cn;
5603         }
5604         
5605         if(this.srButton){
5606             var btn = {
5607                 tag: 'button',
5608                 type: 'button',
5609                 cls: 'navbar-toggle navbar-toggler',
5610                 'data-toggle': 'collapse',
5611                 cn: [
5612                     {
5613                         tag: 'span',
5614                         cls: 'sr-only',
5615                         html: 'Toggle navigation'
5616                     },
5617                     {
5618                         tag: 'span',
5619                         cls: 'icon-bar navbar-toggler-icon'
5620                     },
5621                     {
5622                         tag: 'span',
5623                         cls: 'icon-bar'
5624                     },
5625                     {
5626                         tag: 'span',
5627                         cls: 'icon-bar'
5628                     }
5629                 ]
5630             };
5631             
5632             cn.push( Roo.bootstrap.version == 4 ? btn : {
5633                 tag: 'div',
5634                 cls: 'navbar-header',
5635                 cn: [
5636                     btn
5637                 ]
5638             });
5639         }
5640         
5641         cn.push({
5642             tag: 'div',
5643             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5644             cn : []
5645         });
5646         
5647         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5648         
5649         if (['light','white'].indexOf(this.weight) > -1) {
5650             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5651         }
5652         cfg.cls += ' bg-' + this.weight;
5653         
5654         
5655         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5656             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5657             
5658             // tag can override this..
5659             
5660             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5661         }
5662         
5663         if (this.brand !== '') {
5664             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5665             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5666                 tag: 'a',
5667                 href: this.brand_href ? this.brand_href : '#',
5668                 cls: 'navbar-brand',
5669                 cn: [
5670                 this.brand
5671                 ]
5672             });
5673         }
5674         
5675         if(this.main){
5676             cfg.cls += ' main-nav';
5677         }
5678         
5679         
5680         return cfg;
5681
5682         
5683     },
5684     getHeaderChildContainer : function()
5685     {
5686         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5687             return this.el.select('.navbar-header',true).first();
5688         }
5689         
5690         return this.getChildContainer();
5691     },
5692     
5693     getChildContainer : function()
5694     {
5695          
5696         return this.el.select('.roo-navbar-collapse',true).first();
5697          
5698         
5699     },
5700     
5701     initEvents : function()
5702     {
5703         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5704         
5705         if (this.autohide) {
5706             
5707             var prevScroll = 0;
5708             var ft = this.el;
5709             
5710             Roo.get(document).on('scroll',function(e) {
5711                 var ns = Roo.get(document).getScroll().top;
5712                 var os = prevScroll;
5713                 prevScroll = ns;
5714                 
5715                 if(ns > os){
5716                     ft.removeClass('slideDown');
5717                     ft.addClass('slideUp');
5718                     return;
5719                 }
5720                 ft.removeClass('slideUp');
5721                 ft.addClass('slideDown');
5722                  
5723               
5724           },this);
5725         }
5726     }    
5727     
5728 });
5729
5730
5731
5732  
5733
5734  /*
5735  * - LGPL
5736  *
5737  * navbar
5738  * 
5739  */
5740
5741 /**
5742  * @class Roo.bootstrap.NavSidebar
5743  * @extends Roo.bootstrap.Navbar
5744  * Bootstrap Sidebar class
5745  * 
5746  * @constructor
5747  * Create a new Sidebar
5748  * @param {Object} config The config object
5749  */
5750
5751
5752 Roo.bootstrap.NavSidebar = function(config){
5753     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5754 };
5755
5756 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5757     
5758     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5759     
5760     getAutoCreate : function(){
5761         
5762         
5763         return  {
5764             tag: 'div',
5765             cls: 'sidebar sidebar-nav'
5766         };
5767     
5768         
5769     }
5770     
5771     
5772     
5773 });
5774
5775
5776
5777  
5778
5779  /*
5780  * - LGPL
5781  *
5782  * nav group
5783  * 
5784  */
5785
5786 /**
5787  * @class Roo.bootstrap.NavGroup
5788  * @extends Roo.bootstrap.Component
5789  * Bootstrap NavGroup class
5790  * @cfg {String} align (left|right)
5791  * @cfg {Boolean} inverse
5792  * @cfg {String} type (nav|pills|tab) default nav
5793  * @cfg {String} navId - reference Id for navbar.
5794  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5795  * 
5796  * @constructor
5797  * Create a new nav group
5798  * @param {Object} config The config object
5799  */
5800
5801 Roo.bootstrap.NavGroup = function(config){
5802     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5803     this.navItems = [];
5804    
5805     Roo.bootstrap.NavGroup.register(this);
5806      this.addEvents({
5807         /**
5808              * @event changed
5809              * Fires when the active item changes
5810              * @param {Roo.bootstrap.NavGroup} this
5811              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5812              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5813          */
5814         'changed': true
5815      });
5816     
5817 };
5818
5819 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5820     
5821     align: '',
5822     inverse: false,
5823     form: false,
5824     type: 'nav',
5825     navId : '',
5826     // private
5827     pilltype : true,
5828     
5829     navItems : false, 
5830     
5831     getAutoCreate : function()
5832     {
5833         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5834         
5835         cfg = {
5836             tag : 'ul',
5837             cls: 'nav' 
5838         };
5839         if (Roo.bootstrap.version == 4) {
5840             if (['tabs','pills'].indexOf(this.type) != -1) {
5841                 cfg.cls += ' nav-' + this.type; 
5842             } else {
5843                 // trying to remove so header bar can right align top?
5844                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5845                     // do not use on header bar... 
5846                     cfg.cls += ' navbar-nav';
5847                 }
5848             }
5849             
5850         } else {
5851             if (['tabs','pills'].indexOf(this.type) != -1) {
5852                 cfg.cls += ' nav-' + this.type
5853             } else {
5854                 if (this.type !== 'nav') {
5855                     Roo.log('nav type must be nav/tabs/pills')
5856                 }
5857                 cfg.cls += ' navbar-nav'
5858             }
5859         }
5860         
5861         if (this.parent() && this.parent().sidebar) {
5862             cfg = {
5863                 tag: 'ul',
5864                 cls: 'dashboard-menu sidebar-menu'
5865             };
5866             
5867             return cfg;
5868         }
5869         
5870         if (this.form === true) {
5871             cfg = {
5872                 tag: 'form',
5873                 cls: 'navbar-form form-inline'
5874             };
5875             //nav navbar-right ml-md-auto
5876             if (this.align === 'right') {
5877                 cfg.cls += ' navbar-right ml-md-auto';
5878             } else {
5879                 cfg.cls += ' navbar-left';
5880             }
5881         }
5882         
5883         if (this.align === 'right') {
5884             cfg.cls += ' navbar-right ml-md-auto';
5885         } else {
5886             cfg.cls += ' mr-auto';
5887         }
5888         
5889         if (this.inverse) {
5890             cfg.cls += ' navbar-inverse';
5891             
5892         }
5893         
5894         
5895         return cfg;
5896     },
5897     /**
5898     * sets the active Navigation item
5899     * @param {Roo.bootstrap.NavItem} the new current navitem
5900     */
5901     setActiveItem : function(item)
5902     {
5903         var prev = false;
5904         Roo.each(this.navItems, function(v){
5905             if (v == item) {
5906                 return ;
5907             }
5908             if (v.isActive()) {
5909                 v.setActive(false, true);
5910                 prev = v;
5911                 
5912             }
5913             
5914         });
5915
5916         item.setActive(true, true);
5917         this.fireEvent('changed', this, item, prev);
5918         
5919         
5920     },
5921     /**
5922     * gets the active Navigation item
5923     * @return {Roo.bootstrap.NavItem} the current navitem
5924     */
5925     getActive : function()
5926     {
5927         
5928         var prev = false;
5929         Roo.each(this.navItems, function(v){
5930             
5931             if (v.isActive()) {
5932                 prev = v;
5933                 
5934             }
5935             
5936         });
5937         return prev;
5938     },
5939     
5940     indexOfNav : function()
5941     {
5942         
5943         var prev = false;
5944         Roo.each(this.navItems, function(v,i){
5945             
5946             if (v.isActive()) {
5947                 prev = i;
5948                 
5949             }
5950             
5951         });
5952         return prev;
5953     },
5954     /**
5955     * adds a Navigation item
5956     * @param {Roo.bootstrap.NavItem} the navitem to add
5957     */
5958     addItem : function(cfg)
5959     {
5960         if (this.form && Roo.bootstrap.version == 4) {
5961             cfg.tag = 'div';
5962         }
5963         var cn = new Roo.bootstrap.NavItem(cfg);
5964         this.register(cn);
5965         cn.parentId = this.id;
5966         cn.onRender(this.el, null);
5967         return cn;
5968     },
5969     /**
5970     * register a Navigation item
5971     * @param {Roo.bootstrap.NavItem} the navitem to add
5972     */
5973     register : function(item)
5974     {
5975         this.navItems.push( item);
5976         item.navId = this.navId;
5977     
5978     },
5979     
5980     /**
5981     * clear all the Navigation item
5982     */
5983    
5984     clearAll : function()
5985     {
5986         this.navItems = [];
5987         this.el.dom.innerHTML = '';
5988     },
5989     
5990     getNavItem: function(tabId)
5991     {
5992         var ret = false;
5993         Roo.each(this.navItems, function(e) {
5994             if (e.tabId == tabId) {
5995                ret =  e;
5996                return false;
5997             }
5998             return true;
5999             
6000         });
6001         return ret;
6002     },
6003     
6004     setActiveNext : function()
6005     {
6006         var i = this.indexOfNav(this.getActive());
6007         if (i > this.navItems.length) {
6008             return;
6009         }
6010         this.setActiveItem(this.navItems[i+1]);
6011     },
6012     setActivePrev : function()
6013     {
6014         var i = this.indexOfNav(this.getActive());
6015         if (i  < 1) {
6016             return;
6017         }
6018         this.setActiveItem(this.navItems[i-1]);
6019     },
6020     clearWasActive : function(except) {
6021         Roo.each(this.navItems, function(e) {
6022             if (e.tabId != except.tabId && e.was_active) {
6023                e.was_active = false;
6024                return false;
6025             }
6026             return true;
6027             
6028         });
6029     },
6030     getWasActive : function ()
6031     {
6032         var r = false;
6033         Roo.each(this.navItems, function(e) {
6034             if (e.was_active) {
6035                r = e;
6036                return false;
6037             }
6038             return true;
6039             
6040         });
6041         return r;
6042     }
6043     
6044     
6045 });
6046
6047  
6048 Roo.apply(Roo.bootstrap.NavGroup, {
6049     
6050     groups: {},
6051      /**
6052     * register a Navigation Group
6053     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6054     */
6055     register : function(navgrp)
6056     {
6057         this.groups[navgrp.navId] = navgrp;
6058         
6059     },
6060     /**
6061     * fetch a Navigation Group based on the navigation ID
6062     * @param {string} the navgroup to add
6063     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6064     */
6065     get: function(navId) {
6066         if (typeof(this.groups[navId]) == 'undefined') {
6067             return false;
6068             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6069         }
6070         return this.groups[navId] ;
6071     }
6072     
6073     
6074     
6075 });
6076
6077  /*
6078  * - LGPL
6079  *
6080  * row
6081  * 
6082  */
6083
6084 /**
6085  * @class Roo.bootstrap.NavItem
6086  * @extends Roo.bootstrap.Component
6087  * Bootstrap Navbar.NavItem class
6088  * @cfg {String} href  link to
6089  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6090  * @cfg {Boolean} button_outline show and outlined button
6091  * @cfg {String} html content of button
6092  * @cfg {String} badge text inside badge
6093  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6094  * @cfg {String} glyphicon DEPRICATED - use fa
6095  * @cfg {String} icon DEPRICATED - use fa
6096  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6097  * @cfg {Boolean} active Is item active
6098  * @cfg {Boolean} disabled Is item disabled
6099  * @cfg {String} linkcls  Link Class
6100  * @cfg {Boolean} preventDefault (true | false) default false
6101  * @cfg {String} tabId the tab that this item activates.
6102  * @cfg {String} tagtype (a|span) render as a href or span?
6103  * @cfg {Boolean} animateRef (true|false) link to element default false  
6104   
6105  * @constructor
6106  * Create a new Navbar Item
6107  * @param {Object} config The config object
6108  */
6109 Roo.bootstrap.NavItem = function(config){
6110     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6111     this.addEvents({
6112         // raw events
6113         /**
6114          * @event click
6115          * The raw click event for the entire grid.
6116          * @param {Roo.EventObject} e
6117          */
6118         "click" : true,
6119          /**
6120             * @event changed
6121             * Fires when the active item active state changes
6122             * @param {Roo.bootstrap.NavItem} this
6123             * @param {boolean} state the new state
6124              
6125          */
6126         'changed': true,
6127         /**
6128             * @event scrollto
6129             * Fires when scroll to element
6130             * @param {Roo.bootstrap.NavItem} this
6131             * @param {Object} options
6132             * @param {Roo.EventObject} e
6133              
6134          */
6135         'scrollto': true
6136     });
6137    
6138 };
6139
6140 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6141     
6142     href: false,
6143     html: '',
6144     badge: '',
6145     icon: false,
6146     fa : false,
6147     glyphicon: false,
6148     active: false,
6149     preventDefault : false,
6150     tabId : false,
6151     tagtype : 'a',
6152     tag: 'li',
6153     disabled : false,
6154     animateRef : false,
6155     was_active : false,
6156     button_weight : '',
6157     button_outline : false,
6158     linkcls : '',
6159     navLink: false,
6160     
6161     getAutoCreate : function(){
6162          
6163         var cfg = {
6164             tag: this.tag,
6165             cls: 'nav-item'
6166         };
6167         
6168         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6169         
6170         if (this.active) {
6171             cfg.cls +=  ' active' ;
6172         }
6173         if (this.disabled) {
6174             cfg.cls += ' disabled';
6175         }
6176         
6177         // BS4 only?
6178         if (this.button_weight.length) {
6179             cfg.tag = this.href ? 'a' : 'button';
6180             cfg.html = this.html || '';
6181             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6182             if (this.href) {
6183                 cfg.href = this.href;
6184             }
6185             if (this.fa) {
6186                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6187             } else {
6188                 cfg.cls += " nav-html";
6189             }
6190             
6191             // menu .. should add dropdown-menu class - so no need for carat..
6192             
6193             if (this.badge !== '') {
6194                  
6195                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6196             }
6197             return cfg;
6198         }
6199         
6200         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6201             cfg.cn = [
6202                 {
6203                     tag: this.tagtype,
6204                     href : this.href || "#",
6205                     html: this.html || '',
6206                     cls : ''
6207                 }
6208             ];
6209             if (this.tagtype == 'a') {
6210                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6211         
6212             }
6213             if (this.icon) {
6214                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6215             } else  if (this.fa) {
6216                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6217             } else if(this.glyphicon) {
6218                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6219             } else {
6220                 cfg.cn[0].cls += " nav-html";
6221             }
6222             
6223             if (this.menu) {
6224                 cfg.cn[0].html += " <span class='caret'></span>";
6225              
6226             }
6227             
6228             if (this.badge !== '') {
6229                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6230             }
6231         }
6232         
6233         
6234         
6235         return cfg;
6236     },
6237     onRender : function(ct, position)
6238     {
6239        // Roo.log("Call onRender: " + this.xtype);
6240         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6241             this.tag = 'div';
6242         }
6243         
6244         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6245         this.navLink = this.el.select('.nav-link',true).first();
6246         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6247         return ret;
6248     },
6249       
6250     
6251     initEvents: function() 
6252     {
6253         if (typeof (this.menu) != 'undefined') {
6254             this.menu.parentType = this.xtype;
6255             this.menu.triggerEl = this.el;
6256             this.menu = this.addxtype(Roo.apply({}, this.menu));
6257         }
6258         
6259         this.el.on('click', this.onClick, this);
6260         
6261         //if(this.tagtype == 'span'){
6262         //    this.el.select('span',true).on('click', this.onClick, this);
6263         //}
6264        
6265         // at this point parent should be available..
6266         this.parent().register(this);
6267     },
6268     
6269     onClick : function(e)
6270     {
6271         if (e.getTarget('.dropdown-menu-item')) {
6272             // did you click on a menu itemm.... - then don't trigger onclick..
6273             return;
6274         }
6275         
6276         if(
6277                 this.preventDefault || 
6278                 this.href == '#' 
6279         ){
6280             Roo.log("NavItem - prevent Default?");
6281             e.preventDefault();
6282         }
6283         
6284         if (this.disabled) {
6285             return;
6286         }
6287         
6288         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6289         if (tg && tg.transition) {
6290             Roo.log("waiting for the transitionend");
6291             return;
6292         }
6293         
6294         
6295         
6296         //Roo.log("fire event clicked");
6297         if(this.fireEvent('click', this, e) === false){
6298             return;
6299         };
6300         
6301         if(this.tagtype == 'span'){
6302             return;
6303         }
6304         
6305         //Roo.log(this.href);
6306         var ael = this.el.select('a',true).first();
6307         //Roo.log(ael);
6308         
6309         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6310             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6311             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6312                 return; // ignore... - it's a 'hash' to another page.
6313             }
6314             Roo.log("NavItem - prevent Default?");
6315             e.preventDefault();
6316             this.scrollToElement(e);
6317         }
6318         
6319         
6320         var p =  this.parent();
6321    
6322         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6323             if (typeof(p.setActiveItem) !== 'undefined') {
6324                 p.setActiveItem(this);
6325             }
6326         }
6327         
6328         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6329         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6330             // remove the collapsed menu expand...
6331             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6332         }
6333     },
6334     
6335     isActive: function () {
6336         return this.active
6337     },
6338     setActive : function(state, fire, is_was_active)
6339     {
6340         if (this.active && !state && this.navId) {
6341             this.was_active = true;
6342             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6343             if (nv) {
6344                 nv.clearWasActive(this);
6345             }
6346             
6347         }
6348         this.active = state;
6349         
6350         if (!state ) {
6351             this.el.removeClass('active');
6352             this.navLink ? this.navLink.removeClass('active') : false;
6353         } else if (!this.el.hasClass('active')) {
6354             
6355             this.el.addClass('active');
6356             if (Roo.bootstrap.version == 4 && this.navLink ) {
6357                 this.navLink.addClass('active');
6358             }
6359             
6360         }
6361         if (fire) {
6362             this.fireEvent('changed', this, state);
6363         }
6364         
6365         // show a panel if it's registered and related..
6366         
6367         if (!this.navId || !this.tabId || !state || is_was_active) {
6368             return;
6369         }
6370         
6371         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6372         if (!tg) {
6373             return;
6374         }
6375         var pan = tg.getPanelByName(this.tabId);
6376         if (!pan) {
6377             return;
6378         }
6379         // if we can not flip to new panel - go back to old nav highlight..
6380         if (false == tg.showPanel(pan)) {
6381             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6382             if (nv) {
6383                 var onav = nv.getWasActive();
6384                 if (onav) {
6385                     onav.setActive(true, false, true);
6386                 }
6387             }
6388             
6389         }
6390         
6391         
6392         
6393     },
6394      // this should not be here...
6395     setDisabled : function(state)
6396     {
6397         this.disabled = state;
6398         if (!state ) {
6399             this.el.removeClass('disabled');
6400         } else if (!this.el.hasClass('disabled')) {
6401             this.el.addClass('disabled');
6402         }
6403         
6404     },
6405     
6406     /**
6407      * Fetch the element to display the tooltip on.
6408      * @return {Roo.Element} defaults to this.el
6409      */
6410     tooltipEl : function()
6411     {
6412         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6413     },
6414     
6415     scrollToElement : function(e)
6416     {
6417         var c = document.body;
6418         
6419         /*
6420          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6421          */
6422         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6423             c = document.documentElement;
6424         }
6425         
6426         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6427         
6428         if(!target){
6429             return;
6430         }
6431
6432         var o = target.calcOffsetsTo(c);
6433         
6434         var options = {
6435             target : target,
6436             value : o[1]
6437         };
6438         
6439         this.fireEvent('scrollto', this, options, e);
6440         
6441         Roo.get(c).scrollTo('top', options.value, true);
6442         
6443         return;
6444     },
6445     /**
6446      * Set the HTML (text content) of the item
6447      * @param {string} html  content for the nav item
6448      */
6449     setHtml : function(html)
6450     {
6451         this.html = html;
6452         this.htmlEl.dom.innerHTML = html;
6453         
6454     } 
6455 });
6456  
6457
6458  /*
6459  * - LGPL
6460  *
6461  * sidebar item
6462  *
6463  *  li
6464  *    <span> icon </span>
6465  *    <span> text </span>
6466  *    <span>badge </span>
6467  */
6468
6469 /**
6470  * @class Roo.bootstrap.NavSidebarItem
6471  * @extends Roo.bootstrap.NavItem
6472  * Bootstrap Navbar.NavSidebarItem class
6473  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6474  * {Boolean} open is the menu open
6475  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6476  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6477  * {String} buttonSize (sm|md|lg)the extra classes for the button
6478  * {Boolean} showArrow show arrow next to the text (default true)
6479  * @constructor
6480  * Create a new Navbar Button
6481  * @param {Object} config The config object
6482  */
6483 Roo.bootstrap.NavSidebarItem = function(config){
6484     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6485     this.addEvents({
6486         // raw events
6487         /**
6488          * @event click
6489          * The raw click event for the entire grid.
6490          * @param {Roo.EventObject} e
6491          */
6492         "click" : true,
6493          /**
6494             * @event changed
6495             * Fires when the active item active state changes
6496             * @param {Roo.bootstrap.NavSidebarItem} this
6497             * @param {boolean} state the new state
6498              
6499          */
6500         'changed': true
6501     });
6502    
6503 };
6504
6505 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6506     
6507     badgeWeight : 'default',
6508     
6509     open: false,
6510     
6511     buttonView : false,
6512     
6513     buttonWeight : 'default',
6514     
6515     buttonSize : 'md',
6516     
6517     showArrow : true,
6518     
6519     getAutoCreate : function(){
6520         
6521         
6522         var a = {
6523                 tag: 'a',
6524                 href : this.href || '#',
6525                 cls: '',
6526                 html : '',
6527                 cn : []
6528         };
6529         
6530         if(this.buttonView){
6531             a = {
6532                 tag: 'button',
6533                 href : this.href || '#',
6534                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6535                 html : this.html,
6536                 cn : []
6537             };
6538         }
6539         
6540         var cfg = {
6541             tag: 'li',
6542             cls: '',
6543             cn: [ a ]
6544         };
6545         
6546         if (this.active) {
6547             cfg.cls += ' active';
6548         }
6549         
6550         if (this.disabled) {
6551             cfg.cls += ' disabled';
6552         }
6553         if (this.open) {
6554             cfg.cls += ' open x-open';
6555         }
6556         // left icon..
6557         if (this.glyphicon || this.icon) {
6558             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6559             a.cn.push({ tag : 'i', cls : c }) ;
6560         }
6561         
6562         if(!this.buttonView){
6563             var span = {
6564                 tag: 'span',
6565                 html : this.html || ''
6566             };
6567
6568             a.cn.push(span);
6569             
6570         }
6571         
6572         if (this.badge !== '') {
6573             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6574         }
6575         
6576         if (this.menu) {
6577             
6578             if(this.showArrow){
6579                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6580             }
6581             
6582             a.cls += ' dropdown-toggle treeview' ;
6583         }
6584         
6585         return cfg;
6586     },
6587     
6588     initEvents : function()
6589     { 
6590         if (typeof (this.menu) != 'undefined') {
6591             this.menu.parentType = this.xtype;
6592             this.menu.triggerEl = this.el;
6593             this.menu = this.addxtype(Roo.apply({}, this.menu));
6594         }
6595         
6596         this.el.on('click', this.onClick, this);
6597         
6598         if(this.badge !== ''){
6599             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6600         }
6601         
6602     },
6603     
6604     onClick : function(e)
6605     {
6606         if(this.disabled){
6607             e.preventDefault();
6608             return;
6609         }
6610         
6611         if(this.preventDefault){
6612             e.preventDefault();
6613         }
6614         
6615         this.fireEvent('click', this, e);
6616     },
6617     
6618     disable : function()
6619     {
6620         this.setDisabled(true);
6621     },
6622     
6623     enable : function()
6624     {
6625         this.setDisabled(false);
6626     },
6627     
6628     setDisabled : function(state)
6629     {
6630         if(this.disabled == state){
6631             return;
6632         }
6633         
6634         this.disabled = state;
6635         
6636         if (state) {
6637             this.el.addClass('disabled');
6638             return;
6639         }
6640         
6641         this.el.removeClass('disabled');
6642         
6643         return;
6644     },
6645     
6646     setActive : function(state)
6647     {
6648         if(this.active == state){
6649             return;
6650         }
6651         
6652         this.active = state;
6653         
6654         if (state) {
6655             this.el.addClass('active');
6656             return;
6657         }
6658         
6659         this.el.removeClass('active');
6660         
6661         return;
6662     },
6663     
6664     isActive: function () 
6665     {
6666         return this.active;
6667     },
6668     
6669     setBadge : function(str)
6670     {
6671         if(!this.badgeEl){
6672             return;
6673         }
6674         
6675         this.badgeEl.dom.innerHTML = str;
6676     }
6677     
6678    
6679      
6680  
6681 });
6682  
6683
6684  /*
6685  * - LGPL
6686  *
6687  *  Breadcrumb Nav
6688  * 
6689  */
6690 Roo.namespace('Roo.bootstrap.breadcrumb');
6691
6692
6693 /**
6694  * @class Roo.bootstrap.breadcrumb.Nav
6695  * @extends Roo.bootstrap.Component
6696  * Bootstrap Breadcrumb Nav Class
6697  *  
6698  * @children Roo.bootstrap.breadcrumb.Item
6699  * 
6700  * @constructor
6701  * Create a new breadcrumb.Nav
6702  * @param {Object} config The config object
6703  */
6704
6705
6706 Roo.bootstrap.breadcrumb.Nav = function(config){
6707     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6708     
6709     
6710 };
6711
6712 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6713     
6714     getAutoCreate : function()
6715     {
6716
6717         var cfg = {
6718             tag: 'nav',
6719             cn : [
6720                 {
6721                     tag : 'ol',
6722                     cls : 'breadcrumb'
6723                 }
6724             ]
6725             
6726         };
6727           
6728         return cfg;
6729     },
6730     
6731     initEvents: function()
6732     {
6733         this.olEl = this.el.select('ol',true).first();    
6734     },
6735     getChildContainer : function()
6736     {
6737         return this.olEl;  
6738     }
6739     
6740 });
6741
6742  /*
6743  * - LGPL
6744  *
6745  *  Breadcrumb Item
6746  * 
6747  */
6748
6749
6750 /**
6751  * @class Roo.bootstrap.breadcrumb.Nav
6752  * @extends Roo.bootstrap.Component
6753  * Bootstrap Breadcrumb Nav Class
6754  *  
6755  * @children Roo.bootstrap.breadcrumb.Component
6756  * @cfg {String} html the content of the link.
6757  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6758  * @cfg {Boolean} active is it active
6759
6760  * 
6761  * @constructor
6762  * Create a new breadcrumb.Nav
6763  * @param {Object} config The config object
6764  */
6765
6766 Roo.bootstrap.breadcrumb.Item = function(config){
6767     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6768     this.addEvents({
6769         // img events
6770         /**
6771          * @event click
6772          * The img click event for the img.
6773          * @param {Roo.EventObject} e
6774          */
6775         "click" : true
6776     });
6777     
6778 };
6779
6780 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6781     
6782     href: false,
6783     html : '',
6784     
6785     getAutoCreate : function()
6786     {
6787
6788         var cfg = {
6789             tag: 'li',
6790             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6791         };
6792         if (this.href !== false) {
6793             cfg.cn = [{
6794                 tag : 'a',
6795                 href : this.href,
6796                 html : this.html
6797             }];
6798         } else {
6799             cfg.html = this.html;
6800         }
6801         
6802         return cfg;
6803     },
6804     
6805     initEvents: function()
6806     {
6807         if (this.href) {
6808             this.el.select('a', true).first().on('click',this.onClick, this)
6809         }
6810         
6811     },
6812     onClick : function(e)
6813     {
6814         e.preventDefault();
6815         this.fireEvent('click',this,  e);
6816     }
6817     
6818 });
6819
6820  /*
6821  * - LGPL
6822  *
6823  * row
6824  * 
6825  */
6826
6827 /**
6828  * @class Roo.bootstrap.Row
6829  * @extends Roo.bootstrap.Component
6830  * Bootstrap Row class (contains columns...)
6831  * 
6832  * @constructor
6833  * Create a new Row
6834  * @param {Object} config The config object
6835  */
6836
6837 Roo.bootstrap.Row = function(config){
6838     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6839 };
6840
6841 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6842     
6843     getAutoCreate : function(){
6844        return {
6845             cls: 'row clearfix'
6846        };
6847     }
6848     
6849     
6850 });
6851
6852  
6853
6854  /*
6855  * - LGPL
6856  *
6857  * pagination
6858  * 
6859  */
6860
6861 /**
6862  * @class Roo.bootstrap.Pagination
6863  * @extends Roo.bootstrap.Component
6864  * Bootstrap Pagination class
6865  * @cfg {String} size xs | sm | md | lg
6866  * @cfg {Boolean} inverse false | true
6867  * 
6868  * @constructor
6869  * Create a new Pagination
6870  * @param {Object} config The config object
6871  */
6872
6873 Roo.bootstrap.Pagination = function(config){
6874     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6875 };
6876
6877 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6878     
6879     cls: false,
6880     size: false,
6881     inverse: false,
6882     
6883     getAutoCreate : function(){
6884         var cfg = {
6885             tag: 'ul',
6886                 cls: 'pagination'
6887         };
6888         if (this.inverse) {
6889             cfg.cls += ' inverse';
6890         }
6891         if (this.html) {
6892             cfg.html=this.html;
6893         }
6894         if (this.cls) {
6895             cfg.cls += " " + this.cls;
6896         }
6897         return cfg;
6898     }
6899    
6900 });
6901
6902  
6903
6904  /*
6905  * - LGPL
6906  *
6907  * Pagination item
6908  * 
6909  */
6910
6911
6912 /**
6913  * @class Roo.bootstrap.PaginationItem
6914  * @extends Roo.bootstrap.Component
6915  * Bootstrap PaginationItem class
6916  * @cfg {String} html text
6917  * @cfg {String} href the link
6918  * @cfg {Boolean} preventDefault (true | false) default true
6919  * @cfg {Boolean} active (true | false) default false
6920  * @cfg {Boolean} disabled default false
6921  * 
6922  * 
6923  * @constructor
6924  * Create a new PaginationItem
6925  * @param {Object} config The config object
6926  */
6927
6928
6929 Roo.bootstrap.PaginationItem = function(config){
6930     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6931     this.addEvents({
6932         // raw events
6933         /**
6934          * @event click
6935          * The raw click event for the entire grid.
6936          * @param {Roo.EventObject} e
6937          */
6938         "click" : true
6939     });
6940 };
6941
6942 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6943     
6944     href : false,
6945     html : false,
6946     preventDefault: true,
6947     active : false,
6948     cls : false,
6949     disabled: false,
6950     
6951     getAutoCreate : function(){
6952         var cfg= {
6953             tag: 'li',
6954             cn: [
6955                 {
6956                     tag : 'a',
6957                     href : this.href ? this.href : '#',
6958                     html : this.html ? this.html : ''
6959                 }
6960             ]
6961         };
6962         
6963         if(this.cls){
6964             cfg.cls = this.cls;
6965         }
6966         
6967         if(this.disabled){
6968             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6969         }
6970         
6971         if(this.active){
6972             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6973         }
6974         
6975         return cfg;
6976     },
6977     
6978     initEvents: function() {
6979         
6980         this.el.on('click', this.onClick, this);
6981         
6982     },
6983     onClick : function(e)
6984     {
6985         Roo.log('PaginationItem on click ');
6986         if(this.preventDefault){
6987             e.preventDefault();
6988         }
6989         
6990         if(this.disabled){
6991             return;
6992         }
6993         
6994         this.fireEvent('click', this, e);
6995     }
6996    
6997 });
6998
6999  
7000
7001  /*
7002  * - LGPL
7003  *
7004  * slider
7005  * 
7006  */
7007
7008
7009 /**
7010  * @class Roo.bootstrap.Slider
7011  * @extends Roo.bootstrap.Component
7012  * Bootstrap Slider class
7013  *    
7014  * @constructor
7015  * Create a new Slider
7016  * @param {Object} config The config object
7017  */
7018
7019 Roo.bootstrap.Slider = function(config){
7020     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7021 };
7022
7023 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7024     
7025     getAutoCreate : function(){
7026         
7027         var cfg = {
7028             tag: 'div',
7029             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7030             cn: [
7031                 {
7032                     tag: 'a',
7033                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7034                 }
7035             ]
7036         };
7037         
7038         return cfg;
7039     }
7040    
7041 });
7042
7043  /*
7044  * Based on:
7045  * Ext JS Library 1.1.1
7046  * Copyright(c) 2006-2007, Ext JS, LLC.
7047  *
7048  * Originally Released Under LGPL - original licence link has changed is not relivant.
7049  *
7050  * Fork - LGPL
7051  * <script type="text/javascript">
7052  */
7053  
7054
7055 /**
7056  * @class Roo.grid.ColumnModel
7057  * @extends Roo.util.Observable
7058  * This is the default implementation of a ColumnModel used by the Grid. It defines
7059  * the columns in the grid.
7060  * <br>Usage:<br>
7061  <pre><code>
7062  var colModel = new Roo.grid.ColumnModel([
7063         {header: "Ticker", width: 60, sortable: true, locked: true},
7064         {header: "Company Name", width: 150, sortable: true},
7065         {header: "Market Cap.", width: 100, sortable: true},
7066         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7067         {header: "Employees", width: 100, sortable: true, resizable: false}
7068  ]);
7069  </code></pre>
7070  * <p>
7071  
7072  * The config options listed for this class are options which may appear in each
7073  * individual column definition.
7074  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7075  * @constructor
7076  * @param {Object} config An Array of column config objects. See this class's
7077  * config objects for details.
7078 */
7079 Roo.grid.ColumnModel = function(config){
7080         /**
7081      * The config passed into the constructor
7082      */
7083     this.config = config;
7084     this.lookup = {};
7085
7086     // if no id, create one
7087     // if the column does not have a dataIndex mapping,
7088     // map it to the order it is in the config
7089     for(var i = 0, len = config.length; i < len; i++){
7090         var c = config[i];
7091         if(typeof c.dataIndex == "undefined"){
7092             c.dataIndex = i;
7093         }
7094         if(typeof c.renderer == "string"){
7095             c.renderer = Roo.util.Format[c.renderer];
7096         }
7097         if(typeof c.id == "undefined"){
7098             c.id = Roo.id();
7099         }
7100         if(c.editor && c.editor.xtype){
7101             c.editor  = Roo.factory(c.editor, Roo.grid);
7102         }
7103         if(c.editor && c.editor.isFormField){
7104             c.editor = new Roo.grid.GridEditor(c.editor);
7105         }
7106         this.lookup[c.id] = c;
7107     }
7108
7109     /**
7110      * The width of columns which have no width specified (defaults to 100)
7111      * @type Number
7112      */
7113     this.defaultWidth = 100;
7114
7115     /**
7116      * Default sortable of columns which have no sortable specified (defaults to false)
7117      * @type Boolean
7118      */
7119     this.defaultSortable = false;
7120
7121     this.addEvents({
7122         /**
7123              * @event widthchange
7124              * Fires when the width of a column changes.
7125              * @param {ColumnModel} this
7126              * @param {Number} columnIndex The column index
7127              * @param {Number} newWidth The new width
7128              */
7129             "widthchange": true,
7130         /**
7131              * @event headerchange
7132              * Fires when the text of a header changes.
7133              * @param {ColumnModel} this
7134              * @param {Number} columnIndex The column index
7135              * @param {Number} newText The new header text
7136              */
7137             "headerchange": true,
7138         /**
7139              * @event hiddenchange
7140              * Fires when a column is hidden or "unhidden".
7141              * @param {ColumnModel} this
7142              * @param {Number} columnIndex The column index
7143              * @param {Boolean} hidden true if hidden, false otherwise
7144              */
7145             "hiddenchange": true,
7146             /**
7147          * @event columnmoved
7148          * Fires when a column is moved.
7149          * @param {ColumnModel} this
7150          * @param {Number} oldIndex
7151          * @param {Number} newIndex
7152          */
7153         "columnmoved" : true,
7154         /**
7155          * @event columlockchange
7156          * Fires when a column's locked state is changed
7157          * @param {ColumnModel} this
7158          * @param {Number} colIndex
7159          * @param {Boolean} locked true if locked
7160          */
7161         "columnlockchange" : true
7162     });
7163     Roo.grid.ColumnModel.superclass.constructor.call(this);
7164 };
7165 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7166     /**
7167      * @cfg {String} header The header text to display in the Grid view.
7168      */
7169     /**
7170      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7171      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7172      * specified, the column's index is used as an index into the Record's data Array.
7173      */
7174     /**
7175      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7176      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7177      */
7178     /**
7179      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7180      * Defaults to the value of the {@link #defaultSortable} property.
7181      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7182      */
7183     /**
7184      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7185      */
7186     /**
7187      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7188      */
7189     /**
7190      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7191      */
7192     /**
7193      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7194      */
7195     /**
7196      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7197      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7198      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7199      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7200      */
7201        /**
7202      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7203      */
7204     /**
7205      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7206      */
7207     /**
7208      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7209      */
7210     /**
7211      * @cfg {String} cursor (Optional)
7212      */
7213     /**
7214      * @cfg {String} tooltip (Optional)
7215      */
7216     /**
7217      * @cfg {Number} xs (Optional)
7218      */
7219     /**
7220      * @cfg {Number} sm (Optional)
7221      */
7222     /**
7223      * @cfg {Number} md (Optional)
7224      */
7225     /**
7226      * @cfg {Number} lg (Optional)
7227      */
7228     /**
7229      * Returns the id of the column at the specified index.
7230      * @param {Number} index The column index
7231      * @return {String} the id
7232      */
7233     getColumnId : function(index){
7234         return this.config[index].id;
7235     },
7236
7237     /**
7238      * Returns the column for a specified id.
7239      * @param {String} id The column id
7240      * @return {Object} the column
7241      */
7242     getColumnById : function(id){
7243         return this.lookup[id];
7244     },
7245
7246     
7247     /**
7248      * Returns the column for a specified dataIndex.
7249      * @param {String} dataIndex The column dataIndex
7250      * @return {Object|Boolean} the column or false if not found
7251      */
7252     getColumnByDataIndex: function(dataIndex){
7253         var index = this.findColumnIndex(dataIndex);
7254         return index > -1 ? this.config[index] : false;
7255     },
7256     
7257     /**
7258      * Returns the index for a specified column id.
7259      * @param {String} id The column id
7260      * @return {Number} the index, or -1 if not found
7261      */
7262     getIndexById : function(id){
7263         for(var i = 0, len = this.config.length; i < len; i++){
7264             if(this.config[i].id == id){
7265                 return i;
7266             }
7267         }
7268         return -1;
7269     },
7270     
7271     /**
7272      * Returns the index for a specified column dataIndex.
7273      * @param {String} dataIndex The column dataIndex
7274      * @return {Number} the index, or -1 if not found
7275      */
7276     
7277     findColumnIndex : function(dataIndex){
7278         for(var i = 0, len = this.config.length; i < len; i++){
7279             if(this.config[i].dataIndex == dataIndex){
7280                 return i;
7281             }
7282         }
7283         return -1;
7284     },
7285     
7286     
7287     moveColumn : function(oldIndex, newIndex){
7288         var c = this.config[oldIndex];
7289         this.config.splice(oldIndex, 1);
7290         this.config.splice(newIndex, 0, c);
7291         this.dataMap = null;
7292         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7293     },
7294
7295     isLocked : function(colIndex){
7296         return this.config[colIndex].locked === true;
7297     },
7298
7299     setLocked : function(colIndex, value, suppressEvent){
7300         if(this.isLocked(colIndex) == value){
7301             return;
7302         }
7303         this.config[colIndex].locked = value;
7304         if(!suppressEvent){
7305             this.fireEvent("columnlockchange", this, colIndex, value);
7306         }
7307     },
7308
7309     getTotalLockedWidth : function(){
7310         var totalWidth = 0;
7311         for(var i = 0; i < this.config.length; i++){
7312             if(this.isLocked(i) && !this.isHidden(i)){
7313                 this.totalWidth += this.getColumnWidth(i);
7314             }
7315         }
7316         return totalWidth;
7317     },
7318
7319     getLockedCount : function(){
7320         for(var i = 0, len = this.config.length; i < len; i++){
7321             if(!this.isLocked(i)){
7322                 return i;
7323             }
7324         }
7325         
7326         return this.config.length;
7327     },
7328
7329     /**
7330      * Returns the number of columns.
7331      * @return {Number}
7332      */
7333     getColumnCount : function(visibleOnly){
7334         if(visibleOnly === true){
7335             var c = 0;
7336             for(var i = 0, len = this.config.length; i < len; i++){
7337                 if(!this.isHidden(i)){
7338                     c++;
7339                 }
7340             }
7341             return c;
7342         }
7343         return this.config.length;
7344     },
7345
7346     /**
7347      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7348      * @param {Function} fn
7349      * @param {Object} scope (optional)
7350      * @return {Array} result
7351      */
7352     getColumnsBy : function(fn, scope){
7353         var r = [];
7354         for(var i = 0, len = this.config.length; i < len; i++){
7355             var c = this.config[i];
7356             if(fn.call(scope||this, c, i) === true){
7357                 r[r.length] = c;
7358             }
7359         }
7360         return r;
7361     },
7362
7363     /**
7364      * Returns true if the specified column is sortable.
7365      * @param {Number} col The column index
7366      * @return {Boolean}
7367      */
7368     isSortable : function(col){
7369         if(typeof this.config[col].sortable == "undefined"){
7370             return this.defaultSortable;
7371         }
7372         return this.config[col].sortable;
7373     },
7374
7375     /**
7376      * Returns the rendering (formatting) function defined for the column.
7377      * @param {Number} col The column index.
7378      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7379      */
7380     getRenderer : function(col){
7381         if(!this.config[col].renderer){
7382             return Roo.grid.ColumnModel.defaultRenderer;
7383         }
7384         return this.config[col].renderer;
7385     },
7386
7387     /**
7388      * Sets the rendering (formatting) function for a column.
7389      * @param {Number} col The column index
7390      * @param {Function} fn The function to use to process the cell's raw data
7391      * to return HTML markup for the grid view. The render function is called with
7392      * the following parameters:<ul>
7393      * <li>Data value.</li>
7394      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7395      * <li>css A CSS style string to apply to the table cell.</li>
7396      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7397      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7398      * <li>Row index</li>
7399      * <li>Column index</li>
7400      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7401      */
7402     setRenderer : function(col, fn){
7403         this.config[col].renderer = fn;
7404     },
7405
7406     /**
7407      * Returns the width for the specified column.
7408      * @param {Number} col The column index
7409      * @return {Number}
7410      */
7411     getColumnWidth : function(col){
7412         return this.config[col].width * 1 || this.defaultWidth;
7413     },
7414
7415     /**
7416      * Sets the width for a column.
7417      * @param {Number} col The column index
7418      * @param {Number} width The new width
7419      */
7420     setColumnWidth : function(col, width, suppressEvent){
7421         this.config[col].width = width;
7422         this.totalWidth = null;
7423         if(!suppressEvent){
7424              this.fireEvent("widthchange", this, col, width);
7425         }
7426     },
7427
7428     /**
7429      * Returns the total width of all columns.
7430      * @param {Boolean} includeHidden True to include hidden column widths
7431      * @return {Number}
7432      */
7433     getTotalWidth : function(includeHidden){
7434         if(!this.totalWidth){
7435             this.totalWidth = 0;
7436             for(var i = 0, len = this.config.length; i < len; i++){
7437                 if(includeHidden || !this.isHidden(i)){
7438                     this.totalWidth += this.getColumnWidth(i);
7439                 }
7440             }
7441         }
7442         return this.totalWidth;
7443     },
7444
7445     /**
7446      * Returns the header for the specified column.
7447      * @param {Number} col The column index
7448      * @return {String}
7449      */
7450     getColumnHeader : function(col){
7451         return this.config[col].header;
7452     },
7453
7454     /**
7455      * Sets the header for a column.
7456      * @param {Number} col The column index
7457      * @param {String} header The new header
7458      */
7459     setColumnHeader : function(col, header){
7460         this.config[col].header = header;
7461         this.fireEvent("headerchange", this, col, header);
7462     },
7463
7464     /**
7465      * Returns the tooltip for the specified column.
7466      * @param {Number} col The column index
7467      * @return {String}
7468      */
7469     getColumnTooltip : function(col){
7470             return this.config[col].tooltip;
7471     },
7472     /**
7473      * Sets the tooltip for a column.
7474      * @param {Number} col The column index
7475      * @param {String} tooltip The new tooltip
7476      */
7477     setColumnTooltip : function(col, tooltip){
7478             this.config[col].tooltip = tooltip;
7479     },
7480
7481     /**
7482      * Returns the dataIndex for the specified column.
7483      * @param {Number} col The column index
7484      * @return {Number}
7485      */
7486     getDataIndex : function(col){
7487         return this.config[col].dataIndex;
7488     },
7489
7490     /**
7491      * Sets the dataIndex for a column.
7492      * @param {Number} col The column index
7493      * @param {Number} dataIndex The new dataIndex
7494      */
7495     setDataIndex : function(col, dataIndex){
7496         this.config[col].dataIndex = dataIndex;
7497     },
7498
7499     
7500     
7501     /**
7502      * Returns true if the cell is editable.
7503      * @param {Number} colIndex The column index
7504      * @param {Number} rowIndex The row index - this is nto actually used..?
7505      * @return {Boolean}
7506      */
7507     isCellEditable : function(colIndex, rowIndex){
7508         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7509     },
7510
7511     /**
7512      * Returns the editor defined for the cell/column.
7513      * return false or null to disable editing.
7514      * @param {Number} colIndex The column index
7515      * @param {Number} rowIndex The row index
7516      * @return {Object}
7517      */
7518     getCellEditor : function(colIndex, rowIndex){
7519         return this.config[colIndex].editor;
7520     },
7521
7522     /**
7523      * Sets if a column is editable.
7524      * @param {Number} col The column index
7525      * @param {Boolean} editable True if the column is editable
7526      */
7527     setEditable : function(col, editable){
7528         this.config[col].editable = editable;
7529     },
7530
7531
7532     /**
7533      * Returns true if the column is hidden.
7534      * @param {Number} colIndex The column index
7535      * @return {Boolean}
7536      */
7537     isHidden : function(colIndex){
7538         return this.config[colIndex].hidden;
7539     },
7540
7541
7542     /**
7543      * Returns true if the column width cannot be changed
7544      */
7545     isFixed : function(colIndex){
7546         return this.config[colIndex].fixed;
7547     },
7548
7549     /**
7550      * Returns true if the column can be resized
7551      * @return {Boolean}
7552      */
7553     isResizable : function(colIndex){
7554         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7555     },
7556     /**
7557      * Sets if a column is hidden.
7558      * @param {Number} colIndex The column index
7559      * @param {Boolean} hidden True if the column is hidden
7560      */
7561     setHidden : function(colIndex, hidden){
7562         this.config[colIndex].hidden = hidden;
7563         this.totalWidth = null;
7564         this.fireEvent("hiddenchange", this, colIndex, hidden);
7565     },
7566
7567     /**
7568      * Sets the editor for a column.
7569      * @param {Number} col The column index
7570      * @param {Object} editor The editor object
7571      */
7572     setEditor : function(col, editor){
7573         this.config[col].editor = editor;
7574     }
7575 });
7576
7577 Roo.grid.ColumnModel.defaultRenderer = function(value)
7578 {
7579     if(typeof value == "object") {
7580         return value;
7581     }
7582         if(typeof value == "string" && value.length < 1){
7583             return "&#160;";
7584         }
7585     
7586         return String.format("{0}", value);
7587 };
7588
7589 // Alias for backwards compatibility
7590 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7591 /*
7592  * Based on:
7593  * Ext JS Library 1.1.1
7594  * Copyright(c) 2006-2007, Ext JS, LLC.
7595  *
7596  * Originally Released Under LGPL - original licence link has changed is not relivant.
7597  *
7598  * Fork - LGPL
7599  * <script type="text/javascript">
7600  */
7601  
7602 /**
7603  * @class Roo.LoadMask
7604  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7605  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7606  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7607  * element's UpdateManager load indicator and will be destroyed after the initial load.
7608  * @constructor
7609  * Create a new LoadMask
7610  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7611  * @param {Object} config The config object
7612  */
7613 Roo.LoadMask = function(el, config){
7614     this.el = Roo.get(el);
7615     Roo.apply(this, config);
7616     if(this.store){
7617         this.store.on('beforeload', this.onBeforeLoad, this);
7618         this.store.on('load', this.onLoad, this);
7619         this.store.on('loadexception', this.onLoadException, this);
7620         this.removeMask = false;
7621     }else{
7622         var um = this.el.getUpdateManager();
7623         um.showLoadIndicator = false; // disable the default indicator
7624         um.on('beforeupdate', this.onBeforeLoad, this);
7625         um.on('update', this.onLoad, this);
7626         um.on('failure', this.onLoad, this);
7627         this.removeMask = true;
7628     }
7629 };
7630
7631 Roo.LoadMask.prototype = {
7632     /**
7633      * @cfg {Boolean} removeMask
7634      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7635      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7636      */
7637     /**
7638      * @cfg {String} msg
7639      * The text to display in a centered loading message box (defaults to 'Loading...')
7640      */
7641     msg : 'Loading...',
7642     /**
7643      * @cfg {String} msgCls
7644      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7645      */
7646     msgCls : 'x-mask-loading',
7647
7648     /**
7649      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7650      * @type Boolean
7651      */
7652     disabled: false,
7653
7654     /**
7655      * Disables the mask to prevent it from being displayed
7656      */
7657     disable : function(){
7658        this.disabled = true;
7659     },
7660
7661     /**
7662      * Enables the mask so that it can be displayed
7663      */
7664     enable : function(){
7665         this.disabled = false;
7666     },
7667     
7668     onLoadException : function()
7669     {
7670         Roo.log(arguments);
7671         
7672         if (typeof(arguments[3]) != 'undefined') {
7673             Roo.MessageBox.alert("Error loading",arguments[3]);
7674         } 
7675         /*
7676         try {
7677             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7678                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7679             }   
7680         } catch(e) {
7681             
7682         }
7683         */
7684     
7685         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7686     },
7687     // private
7688     onLoad : function()
7689     {
7690         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7691     },
7692
7693     // private
7694     onBeforeLoad : function(){
7695         if(!this.disabled){
7696             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7697         }
7698     },
7699
7700     // private
7701     destroy : function(){
7702         if(this.store){
7703             this.store.un('beforeload', this.onBeforeLoad, this);
7704             this.store.un('load', this.onLoad, this);
7705             this.store.un('loadexception', this.onLoadException, this);
7706         }else{
7707             var um = this.el.getUpdateManager();
7708             um.un('beforeupdate', this.onBeforeLoad, this);
7709             um.un('update', this.onLoad, this);
7710             um.un('failure', this.onLoad, this);
7711         }
7712     }
7713 };/*
7714  * - LGPL
7715  *
7716  * table
7717  * 
7718  */
7719
7720 /**
7721  * @class Roo.bootstrap.Table
7722  * @extends Roo.bootstrap.Component
7723  * Bootstrap Table class
7724  * @cfg {String} cls table class
7725  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7726  * @cfg {String} bgcolor Specifies the background color for a table
7727  * @cfg {Number} border Specifies whether the table cells should have borders or not
7728  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7729  * @cfg {Number} cellspacing Specifies the space between cells
7730  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7731  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7732  * @cfg {String} sortable Specifies that the table should be sortable
7733  * @cfg {String} summary Specifies a summary of the content of a table
7734  * @cfg {Number} width Specifies the width of a table
7735  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7736  * 
7737  * @cfg {boolean} striped Should the rows be alternative striped
7738  * @cfg {boolean} bordered Add borders to the table
7739  * @cfg {boolean} hover Add hover highlighting
7740  * @cfg {boolean} condensed Format condensed
7741  * @cfg {boolean} responsive Format condensed
7742  * @cfg {Boolean} loadMask (true|false) default false
7743  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7744  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7745  * @cfg {Boolean} rowSelection (true|false) default false
7746  * @cfg {Boolean} cellSelection (true|false) default false
7747  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7748  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7749  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7750  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7751  
7752  * 
7753  * @constructor
7754  * Create a new Table
7755  * @param {Object} config The config object
7756  */
7757
7758 Roo.bootstrap.Table = function(config){
7759     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7760     
7761   
7762     
7763     // BC...
7764     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7765     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7766     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7767     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7768     
7769     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7770     if (this.sm) {
7771         this.sm.grid = this;
7772         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7773         this.sm = this.selModel;
7774         this.sm.xmodule = this.xmodule || false;
7775     }
7776     
7777     if (this.cm && typeof(this.cm.config) == 'undefined') {
7778         this.colModel = new Roo.grid.ColumnModel(this.cm);
7779         this.cm = this.colModel;
7780         this.cm.xmodule = this.xmodule || false;
7781     }
7782     if (this.store) {
7783         this.store= Roo.factory(this.store, Roo.data);
7784         this.ds = this.store;
7785         this.ds.xmodule = this.xmodule || false;
7786          
7787     }
7788     if (this.footer && this.store) {
7789         this.footer.dataSource = this.ds;
7790         this.footer = Roo.factory(this.footer);
7791     }
7792     
7793     /** @private */
7794     this.addEvents({
7795         /**
7796          * @event cellclick
7797          * Fires when a cell is clicked
7798          * @param {Roo.bootstrap.Table} this
7799          * @param {Roo.Element} el
7800          * @param {Number} rowIndex
7801          * @param {Number} columnIndex
7802          * @param {Roo.EventObject} e
7803          */
7804         "cellclick" : true,
7805         /**
7806          * @event celldblclick
7807          * Fires when a cell is double clicked
7808          * @param {Roo.bootstrap.Table} this
7809          * @param {Roo.Element} el
7810          * @param {Number} rowIndex
7811          * @param {Number} columnIndex
7812          * @param {Roo.EventObject} e
7813          */
7814         "celldblclick" : true,
7815         /**
7816          * @event rowclick
7817          * Fires when a row is clicked
7818          * @param {Roo.bootstrap.Table} this
7819          * @param {Roo.Element} el
7820          * @param {Number} rowIndex
7821          * @param {Roo.EventObject} e
7822          */
7823         "rowclick" : true,
7824         /**
7825          * @event rowdblclick
7826          * Fires when a row is double clicked
7827          * @param {Roo.bootstrap.Table} this
7828          * @param {Roo.Element} el
7829          * @param {Number} rowIndex
7830          * @param {Roo.EventObject} e
7831          */
7832         "rowdblclick" : true,
7833         /**
7834          * @event mouseover
7835          * Fires when a mouseover occur
7836          * @param {Roo.bootstrap.Table} this
7837          * @param {Roo.Element} el
7838          * @param {Number} rowIndex
7839          * @param {Number} columnIndex
7840          * @param {Roo.EventObject} e
7841          */
7842         "mouseover" : true,
7843         /**
7844          * @event mouseout
7845          * Fires when a mouseout occur
7846          * @param {Roo.bootstrap.Table} this
7847          * @param {Roo.Element} el
7848          * @param {Number} rowIndex
7849          * @param {Number} columnIndex
7850          * @param {Roo.EventObject} e
7851          */
7852         "mouseout" : true,
7853         /**
7854          * @event rowclass
7855          * Fires when a row is rendered, so you can change add a style to it.
7856          * @param {Roo.bootstrap.Table} this
7857          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7858          */
7859         'rowclass' : true,
7860           /**
7861          * @event rowsrendered
7862          * Fires when all the  rows have been rendered
7863          * @param {Roo.bootstrap.Table} this
7864          */
7865         'rowsrendered' : true,
7866         /**
7867          * @event contextmenu
7868          * The raw contextmenu event for the entire grid.
7869          * @param {Roo.EventObject} e
7870          */
7871         "contextmenu" : true,
7872         /**
7873          * @event rowcontextmenu
7874          * Fires when a row is right clicked
7875          * @param {Roo.bootstrap.Table} this
7876          * @param {Number} rowIndex
7877          * @param {Roo.EventObject} e
7878          */
7879         "rowcontextmenu" : true,
7880         /**
7881          * @event cellcontextmenu
7882          * Fires when a cell is right clicked
7883          * @param {Roo.bootstrap.Table} this
7884          * @param {Number} rowIndex
7885          * @param {Number} cellIndex
7886          * @param {Roo.EventObject} e
7887          */
7888          "cellcontextmenu" : true,
7889          /**
7890          * @event headercontextmenu
7891          * Fires when a header is right clicked
7892          * @param {Roo.bootstrap.Table} this
7893          * @param {Number} columnIndex
7894          * @param {Roo.EventObject} e
7895          */
7896         "headercontextmenu" : true
7897     });
7898 };
7899
7900 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7901     
7902     cls: false,
7903     align: false,
7904     bgcolor: false,
7905     border: false,
7906     cellpadding: false,
7907     cellspacing: false,
7908     frame: false,
7909     rules: false,
7910     sortable: false,
7911     summary: false,
7912     width: false,
7913     striped : false,
7914     scrollBody : false,
7915     bordered: false,
7916     hover:  false,
7917     condensed : false,
7918     responsive : false,
7919     sm : false,
7920     cm : false,
7921     store : false,
7922     loadMask : false,
7923     footerShow : true,
7924     headerShow : true,
7925   
7926     rowSelection : false,
7927     cellSelection : false,
7928     layout : false,
7929     
7930     // Roo.Element - the tbody
7931     mainBody: false,
7932     // Roo.Element - thead element
7933     mainHead: false,
7934     
7935     container: false, // used by gridpanel...
7936     
7937     lazyLoad : false,
7938     
7939     CSS : Roo.util.CSS,
7940     
7941     auto_hide_footer : false,
7942     
7943     getAutoCreate : function()
7944     {
7945         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7946         
7947         cfg = {
7948             tag: 'table',
7949             cls : 'table',
7950             cn : []
7951         };
7952         if (this.scrollBody) {
7953             cfg.cls += ' table-body-fixed';
7954         }    
7955         if (this.striped) {
7956             cfg.cls += ' table-striped';
7957         }
7958         
7959         if (this.hover) {
7960             cfg.cls += ' table-hover';
7961         }
7962         if (this.bordered) {
7963             cfg.cls += ' table-bordered';
7964         }
7965         if (this.condensed) {
7966             cfg.cls += ' table-condensed';
7967         }
7968         if (this.responsive) {
7969             cfg.cls += ' table-responsive';
7970         }
7971         
7972         if (this.cls) {
7973             cfg.cls+=  ' ' +this.cls;
7974         }
7975         
7976         // this lot should be simplifed...
7977         var _t = this;
7978         var cp = [
7979             'align',
7980             'bgcolor',
7981             'border',
7982             'cellpadding',
7983             'cellspacing',
7984             'frame',
7985             'rules',
7986             'sortable',
7987             'summary',
7988             'width'
7989         ].forEach(function(k) {
7990             if (_t[k]) {
7991                 cfg[k] = _t[k];
7992             }
7993         });
7994         
7995         
7996         if (this.layout) {
7997             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7998         }
7999         
8000         if(this.store || this.cm){
8001             if(this.headerShow){
8002                 cfg.cn.push(this.renderHeader());
8003             }
8004             
8005             cfg.cn.push(this.renderBody());
8006             
8007             if(this.footerShow){
8008                 cfg.cn.push(this.renderFooter());
8009             }
8010             // where does this come from?
8011             //cfg.cls+=  ' TableGrid';
8012         }
8013         
8014         return { cn : [ cfg ] };
8015     },
8016     
8017     initEvents : function()
8018     {   
8019         if(!this.store || !this.cm){
8020             return;
8021         }
8022         if (this.selModel) {
8023             this.selModel.initEvents();
8024         }
8025         
8026         
8027         //Roo.log('initEvents with ds!!!!');
8028         
8029         this.mainBody = this.el.select('tbody', true).first();
8030         this.mainHead = this.el.select('thead', true).first();
8031         this.mainFoot = this.el.select('tfoot', true).first();
8032         
8033         
8034         
8035         var _this = this;
8036         
8037         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8038             e.on('click', _this.sort, _this);
8039         });
8040         
8041         this.mainBody.on("click", this.onClick, this);
8042         this.mainBody.on("dblclick", this.onDblClick, this);
8043         
8044         // why is this done????? = it breaks dialogs??
8045         //this.parent().el.setStyle('position', 'relative');
8046         
8047         
8048         if (this.footer) {
8049             this.footer.parentId = this.id;
8050             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8051             
8052             if(this.lazyLoad){
8053                 this.el.select('tfoot tr td').first().addClass('hide');
8054             }
8055         } 
8056         
8057         if(this.loadMask) {
8058             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8059         }
8060         
8061         this.store.on('load', this.onLoad, this);
8062         this.store.on('beforeload', this.onBeforeLoad, this);
8063         this.store.on('update', this.onUpdate, this);
8064         this.store.on('add', this.onAdd, this);
8065         this.store.on("clear", this.clear, this);
8066         
8067         this.el.on("contextmenu", this.onContextMenu, this);
8068         
8069         this.mainBody.on('scroll', this.onBodyScroll, this);
8070         
8071         this.cm.on("headerchange", this.onHeaderChange, this);
8072         
8073         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8074         
8075     },
8076     
8077     onContextMenu : function(e, t)
8078     {
8079         this.processEvent("contextmenu", e);
8080     },
8081     
8082     processEvent : function(name, e)
8083     {
8084         if (name != 'touchstart' ) {
8085             this.fireEvent(name, e);    
8086         }
8087         
8088         var t = e.getTarget();
8089         
8090         var cell = Roo.get(t);
8091         
8092         if(!cell){
8093             return;
8094         }
8095         
8096         if(cell.findParent('tfoot', false, true)){
8097             return;
8098         }
8099         
8100         if(cell.findParent('thead', false, true)){
8101             
8102             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8103                 cell = Roo.get(t).findParent('th', false, true);
8104                 if (!cell) {
8105                     Roo.log("failed to find th in thead?");
8106                     Roo.log(e.getTarget());
8107                     return;
8108                 }
8109             }
8110             
8111             var cellIndex = cell.dom.cellIndex;
8112             
8113             var ename = name == 'touchstart' ? 'click' : name;
8114             this.fireEvent("header" + ename, this, cellIndex, e);
8115             
8116             return;
8117         }
8118         
8119         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8120             cell = Roo.get(t).findParent('td', false, true);
8121             if (!cell) {
8122                 Roo.log("failed to find th in tbody?");
8123                 Roo.log(e.getTarget());
8124                 return;
8125             }
8126         }
8127         
8128         var row = cell.findParent('tr', false, true);
8129         var cellIndex = cell.dom.cellIndex;
8130         var rowIndex = row.dom.rowIndex - 1;
8131         
8132         if(row !== false){
8133             
8134             this.fireEvent("row" + name, this, rowIndex, e);
8135             
8136             if(cell !== false){
8137             
8138                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8139             }
8140         }
8141         
8142     },
8143     
8144     onMouseover : function(e, el)
8145     {
8146         var cell = Roo.get(el);
8147         
8148         if(!cell){
8149             return;
8150         }
8151         
8152         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8153             cell = cell.findParent('td', false, true);
8154         }
8155         
8156         var row = cell.findParent('tr', false, true);
8157         var cellIndex = cell.dom.cellIndex;
8158         var rowIndex = row.dom.rowIndex - 1; // start from 0
8159         
8160         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8161         
8162     },
8163     
8164     onMouseout : function(e, el)
8165     {
8166         var cell = Roo.get(el);
8167         
8168         if(!cell){
8169             return;
8170         }
8171         
8172         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8173             cell = cell.findParent('td', false, true);
8174         }
8175         
8176         var row = cell.findParent('tr', false, true);
8177         var cellIndex = cell.dom.cellIndex;
8178         var rowIndex = row.dom.rowIndex - 1; // start from 0
8179         
8180         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8181         
8182     },
8183     
8184     onClick : function(e, el)
8185     {
8186         var cell = Roo.get(el);
8187         
8188         if(!cell || (!this.cellSelection && !this.rowSelection)){
8189             return;
8190         }
8191         
8192         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8193             cell = cell.findParent('td', false, true);
8194         }
8195         
8196         if(!cell || typeof(cell) == 'undefined'){
8197             return;
8198         }
8199         
8200         var row = cell.findParent('tr', false, true);
8201         
8202         if(!row || typeof(row) == 'undefined'){
8203             return;
8204         }
8205         
8206         var cellIndex = cell.dom.cellIndex;
8207         var rowIndex = this.getRowIndex(row);
8208         
8209         // why??? - should these not be based on SelectionModel?
8210         if(this.cellSelection){
8211             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8212         }
8213         
8214         if(this.rowSelection){
8215             this.fireEvent('rowclick', this, row, rowIndex, e);
8216         }
8217         
8218         
8219     },
8220         
8221     onDblClick : function(e,el)
8222     {
8223         var cell = Roo.get(el);
8224         
8225         if(!cell || (!this.cellSelection && !this.rowSelection)){
8226             return;
8227         }
8228         
8229         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8230             cell = cell.findParent('td', false, true);
8231         }
8232         
8233         if(!cell || typeof(cell) == 'undefined'){
8234             return;
8235         }
8236         
8237         var row = cell.findParent('tr', false, true);
8238         
8239         if(!row || typeof(row) == 'undefined'){
8240             return;
8241         }
8242         
8243         var cellIndex = cell.dom.cellIndex;
8244         var rowIndex = this.getRowIndex(row);
8245         
8246         if(this.cellSelection){
8247             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8248         }
8249         
8250         if(this.rowSelection){
8251             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8252         }
8253     },
8254     
8255     sort : function(e,el)
8256     {
8257         var col = Roo.get(el);
8258         
8259         if(!col.hasClass('sortable')){
8260             return;
8261         }
8262         
8263         var sort = col.attr('sort');
8264         var dir = 'ASC';
8265         
8266         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8267             dir = 'DESC';
8268         }
8269         
8270         this.store.sortInfo = {field : sort, direction : dir};
8271         
8272         if (this.footer) {
8273             Roo.log("calling footer first");
8274             this.footer.onClick('first');
8275         } else {
8276         
8277             this.store.load({ params : { start : 0 } });
8278         }
8279     },
8280     
8281     renderHeader : function()
8282     {
8283         var header = {
8284             tag: 'thead',
8285             cn : []
8286         };
8287         
8288         var cm = this.cm;
8289         this.totalWidth = 0;
8290         
8291         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8292             
8293             var config = cm.config[i];
8294             
8295             var c = {
8296                 tag: 'th',
8297                 cls : 'x-hcol-' + i,
8298                 style : '',
8299                 html: cm.getColumnHeader(i)
8300             };
8301             
8302             var hh = '';
8303             
8304             if(typeof(config.sortable) != 'undefined' && config.sortable){
8305                 c.cls = 'sortable';
8306                 c.html = '<i class="glyphicon"></i>' + c.html;
8307             }
8308             
8309             // could use BS4 hidden-..-down 
8310             
8311             if(typeof(config.lgHeader) != 'undefined'){
8312                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8313             }
8314             
8315             if(typeof(config.mdHeader) != 'undefined'){
8316                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8317             }
8318             
8319             if(typeof(config.smHeader) != 'undefined'){
8320                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8321             }
8322             
8323             if(typeof(config.xsHeader) != 'undefined'){
8324                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8325             }
8326             
8327             if(hh.length){
8328                 c.html = hh;
8329             }
8330             
8331             if(typeof(config.tooltip) != 'undefined'){
8332                 c.tooltip = config.tooltip;
8333             }
8334             
8335             if(typeof(config.colspan) != 'undefined'){
8336                 c.colspan = config.colspan;
8337             }
8338             
8339             if(typeof(config.hidden) != 'undefined' && config.hidden){
8340                 c.style += ' display:none;';
8341             }
8342             
8343             if(typeof(config.dataIndex) != 'undefined'){
8344                 c.sort = config.dataIndex;
8345             }
8346             
8347            
8348             
8349             if(typeof(config.align) != 'undefined' && config.align.length){
8350                 c.style += ' text-align:' + config.align + ';';
8351             }
8352             
8353             if(typeof(config.width) != 'undefined'){
8354                 c.style += ' width:' + config.width + 'px;';
8355                 this.totalWidth += config.width;
8356             } else {
8357                 this.totalWidth += 100; // assume minimum of 100 per column?
8358             }
8359             
8360             if(typeof(config.cls) != 'undefined'){
8361                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8362             }
8363             
8364             ['xs','sm','md','lg'].map(function(size){
8365                 
8366                 if(typeof(config[size]) == 'undefined'){
8367                     return;
8368                 }
8369                  
8370                 if (!config[size]) { // 0 = hidden
8371                     // BS 4 '0' is treated as hide that column and below.
8372                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8373                     return;
8374                 }
8375                 
8376                 c.cls += ' col-' + size + '-' + config[size] + (
8377                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8378                 );
8379                 
8380                 
8381             });
8382             
8383             header.cn.push(c)
8384         }
8385         
8386         return header;
8387     },
8388     
8389     renderBody : function()
8390     {
8391         var body = {
8392             tag: 'tbody',
8393             cn : [
8394                 {
8395                     tag: 'tr',
8396                     cn : [
8397                         {
8398                             tag : 'td',
8399                             colspan :  this.cm.getColumnCount()
8400                         }
8401                     ]
8402                 }
8403             ]
8404         };
8405         
8406         return body;
8407     },
8408     
8409     renderFooter : function()
8410     {
8411         var footer = {
8412             tag: 'tfoot',
8413             cn : [
8414                 {
8415                     tag: 'tr',
8416                     cn : [
8417                         {
8418                             tag : 'td',
8419                             colspan :  this.cm.getColumnCount()
8420                         }
8421                     ]
8422                 }
8423             ]
8424         };
8425         
8426         return footer;
8427     },
8428     
8429     
8430     
8431     onLoad : function()
8432     {
8433 //        Roo.log('ds onload');
8434         this.clear();
8435         
8436         var _this = this;
8437         var cm = this.cm;
8438         var ds = this.store;
8439         
8440         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8441             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8442             if (_this.store.sortInfo) {
8443                     
8444                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8445                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8446                 }
8447                 
8448                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8449                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8450                 }
8451             }
8452         });
8453         
8454         var tbody =  this.mainBody;
8455               
8456         if(ds.getCount() > 0){
8457             ds.data.each(function(d,rowIndex){
8458                 var row =  this.renderRow(cm, ds, rowIndex);
8459                 
8460                 tbody.createChild(row);
8461                 
8462                 var _this = this;
8463                 
8464                 if(row.cellObjects.length){
8465                     Roo.each(row.cellObjects, function(r){
8466                         _this.renderCellObject(r);
8467                     })
8468                 }
8469                 
8470             }, this);
8471         }
8472         
8473         var tfoot = this.el.select('tfoot', true).first();
8474         
8475         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8476             
8477             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8478             
8479             var total = this.ds.getTotalCount();
8480             
8481             if(this.footer.pageSize < total){
8482                 this.mainFoot.show();
8483             }
8484         }
8485         
8486         Roo.each(this.el.select('tbody td', true).elements, function(e){
8487             e.on('mouseover', _this.onMouseover, _this);
8488         });
8489         
8490         Roo.each(this.el.select('tbody td', true).elements, function(e){
8491             e.on('mouseout', _this.onMouseout, _this);
8492         });
8493         this.fireEvent('rowsrendered', this);
8494         
8495         this.autoSize();
8496     },
8497     
8498     
8499     onUpdate : function(ds,record)
8500     {
8501         this.refreshRow(record);
8502         this.autoSize();
8503     },
8504     
8505     onRemove : function(ds, record, index, isUpdate){
8506         if(isUpdate !== true){
8507             this.fireEvent("beforerowremoved", this, index, record);
8508         }
8509         var bt = this.mainBody.dom;
8510         
8511         var rows = this.el.select('tbody > tr', true).elements;
8512         
8513         if(typeof(rows[index]) != 'undefined'){
8514             bt.removeChild(rows[index].dom);
8515         }
8516         
8517 //        if(bt.rows[index]){
8518 //            bt.removeChild(bt.rows[index]);
8519 //        }
8520         
8521         if(isUpdate !== true){
8522             //this.stripeRows(index);
8523             //this.syncRowHeights(index, index);
8524             //this.layout();
8525             this.fireEvent("rowremoved", this, index, record);
8526         }
8527     },
8528     
8529     onAdd : function(ds, records, rowIndex)
8530     {
8531         //Roo.log('on Add called');
8532         // - note this does not handle multiple adding very well..
8533         var bt = this.mainBody.dom;
8534         for (var i =0 ; i < records.length;i++) {
8535             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8536             //Roo.log(records[i]);
8537             //Roo.log(this.store.getAt(rowIndex+i));
8538             this.insertRow(this.store, rowIndex + i, false);
8539             return;
8540         }
8541         
8542     },
8543     
8544     
8545     refreshRow : function(record){
8546         var ds = this.store, index;
8547         if(typeof record == 'number'){
8548             index = record;
8549             record = ds.getAt(index);
8550         }else{
8551             index = ds.indexOf(record);
8552             if (index < 0) {
8553                 return; // should not happen - but seems to 
8554             }
8555         }
8556         this.insertRow(ds, index, true);
8557         this.autoSize();
8558         this.onRemove(ds, record, index+1, true);
8559         this.autoSize();
8560         //this.syncRowHeights(index, index);
8561         //this.layout();
8562         this.fireEvent("rowupdated", this, index, record);
8563     },
8564     
8565     insertRow : function(dm, rowIndex, isUpdate){
8566         
8567         if(!isUpdate){
8568             this.fireEvent("beforerowsinserted", this, rowIndex);
8569         }
8570             //var s = this.getScrollState();
8571         var row = this.renderRow(this.cm, this.store, rowIndex);
8572         // insert before rowIndex..
8573         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8574         
8575         var _this = this;
8576                 
8577         if(row.cellObjects.length){
8578             Roo.each(row.cellObjects, function(r){
8579                 _this.renderCellObject(r);
8580             })
8581         }
8582             
8583         if(!isUpdate){
8584             this.fireEvent("rowsinserted", this, rowIndex);
8585             //this.syncRowHeights(firstRow, lastRow);
8586             //this.stripeRows(firstRow);
8587             //this.layout();
8588         }
8589         
8590     },
8591     
8592     
8593     getRowDom : function(rowIndex)
8594     {
8595         var rows = this.el.select('tbody > tr', true).elements;
8596         
8597         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8598         
8599     },
8600     // returns the object tree for a tr..
8601   
8602     
8603     renderRow : function(cm, ds, rowIndex) 
8604     {
8605         var d = ds.getAt(rowIndex);
8606         
8607         var row = {
8608             tag : 'tr',
8609             cls : 'x-row-' + rowIndex,
8610             cn : []
8611         };
8612             
8613         var cellObjects = [];
8614         
8615         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8616             var config = cm.config[i];
8617             
8618             var renderer = cm.getRenderer(i);
8619             var value = '';
8620             var id = false;
8621             
8622             if(typeof(renderer) !== 'undefined'){
8623                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8624             }
8625             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8626             // and are rendered into the cells after the row is rendered - using the id for the element.
8627             
8628             if(typeof(value) === 'object'){
8629                 id = Roo.id();
8630                 cellObjects.push({
8631                     container : id,
8632                     cfg : value 
8633                 })
8634             }
8635             
8636             var rowcfg = {
8637                 record: d,
8638                 rowIndex : rowIndex,
8639                 colIndex : i,
8640                 rowClass : ''
8641             };
8642
8643             this.fireEvent('rowclass', this, rowcfg);
8644             
8645             var td = {
8646                 tag: 'td',
8647                 cls : rowcfg.rowClass + ' x-col-' + i,
8648                 style: '',
8649                 html: (typeof(value) === 'object') ? '' : value
8650             };
8651             
8652             if (id) {
8653                 td.id = id;
8654             }
8655             
8656             if(typeof(config.colspan) != 'undefined'){
8657                 td.colspan = config.colspan;
8658             }
8659             
8660             if(typeof(config.hidden) != 'undefined' && config.hidden){
8661                 td.style += ' display:none;';
8662             }
8663             
8664             if(typeof(config.align) != 'undefined' && config.align.length){
8665                 td.style += ' text-align:' + config.align + ';';
8666             }
8667             if(typeof(config.valign) != 'undefined' && config.valign.length){
8668                 td.style += ' vertical-align:' + config.valign + ';';
8669             }
8670             
8671             if(typeof(config.width) != 'undefined'){
8672                 td.style += ' width:' +  config.width + 'px;';
8673             }
8674             
8675             if(typeof(config.cursor) != 'undefined'){
8676                 td.style += ' cursor:' +  config.cursor + ';';
8677             }
8678             
8679             if(typeof(config.cls) != 'undefined'){
8680                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8681             }
8682             
8683             ['xs','sm','md','lg'].map(function(size){
8684                 
8685                 if(typeof(config[size]) == 'undefined'){
8686                     return;
8687                 }
8688                 
8689                 
8690                   
8691                 if (!config[size]) { // 0 = hidden
8692                     // BS 4 '0' is treated as hide that column and below.
8693                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8694                     return;
8695                 }
8696                 
8697                 td.cls += ' col-' + size + '-' + config[size] + (
8698                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8699                 );
8700                  
8701
8702             });
8703             
8704             row.cn.push(td);
8705            
8706         }
8707         
8708         row.cellObjects = cellObjects;
8709         
8710         return row;
8711           
8712     },
8713     
8714     
8715     
8716     onBeforeLoad : function()
8717     {
8718         
8719     },
8720      /**
8721      * Remove all rows
8722      */
8723     clear : function()
8724     {
8725         this.el.select('tbody', true).first().dom.innerHTML = '';
8726     },
8727     /**
8728      * Show or hide a row.
8729      * @param {Number} rowIndex to show or hide
8730      * @param {Boolean} state hide
8731      */
8732     setRowVisibility : function(rowIndex, state)
8733     {
8734         var bt = this.mainBody.dom;
8735         
8736         var rows = this.el.select('tbody > tr', true).elements;
8737         
8738         if(typeof(rows[rowIndex]) == 'undefined'){
8739             return;
8740         }
8741         rows[rowIndex].dom.style.display = state ? '' : 'none';
8742     },
8743     
8744     
8745     getSelectionModel : function(){
8746         if(!this.selModel){
8747             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8748         }
8749         return this.selModel;
8750     },
8751     /*
8752      * Render the Roo.bootstrap object from renderder
8753      */
8754     renderCellObject : function(r)
8755     {
8756         var _this = this;
8757         
8758         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8759         
8760         var t = r.cfg.render(r.container);
8761         
8762         if(r.cfg.cn){
8763             Roo.each(r.cfg.cn, function(c){
8764                 var child = {
8765                     container: t.getChildContainer(),
8766                     cfg: c
8767                 };
8768                 _this.renderCellObject(child);
8769             })
8770         }
8771     },
8772     
8773     getRowIndex : function(row)
8774     {
8775         var rowIndex = -1;
8776         
8777         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8778             if(el != row){
8779                 return;
8780             }
8781             
8782             rowIndex = index;
8783         });
8784         
8785         return rowIndex;
8786     },
8787      /**
8788      * Returns the grid's underlying element = used by panel.Grid
8789      * @return {Element} The element
8790      */
8791     getGridEl : function(){
8792         return this.el;
8793     },
8794      /**
8795      * Forces a resize - used by panel.Grid
8796      * @return {Element} The element
8797      */
8798     autoSize : function()
8799     {
8800         //var ctr = Roo.get(this.container.dom.parentElement);
8801         var ctr = Roo.get(this.el.dom);
8802         
8803         var thd = this.getGridEl().select('thead',true).first();
8804         var tbd = this.getGridEl().select('tbody', true).first();
8805         var tfd = this.getGridEl().select('tfoot', true).first();
8806         
8807         var cw = ctr.getWidth();
8808         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8809         
8810         if (tbd) {
8811             
8812             tbd.setWidth(ctr.getWidth());
8813             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8814             // this needs fixing for various usage - currently only hydra job advers I think..
8815             //tdb.setHeight(
8816             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8817             //); 
8818             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8819             cw -= barsize;
8820         }
8821         cw = Math.max(cw, this.totalWidth);
8822         this.getGridEl().select('tbody tr',true).setWidth(cw);
8823         
8824         // resize 'expandable coloumn?
8825         
8826         return; // we doe not have a view in this design..
8827         
8828     },
8829     onBodyScroll: function()
8830     {
8831         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8832         if(this.mainHead){
8833             this.mainHead.setStyle({
8834                 'position' : 'relative',
8835                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8836             });
8837         }
8838         
8839         if(this.lazyLoad){
8840             
8841             var scrollHeight = this.mainBody.dom.scrollHeight;
8842             
8843             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8844             
8845             var height = this.mainBody.getHeight();
8846             
8847             if(scrollHeight - height == scrollTop) {
8848                 
8849                 var total = this.ds.getTotalCount();
8850                 
8851                 if(this.footer.cursor + this.footer.pageSize < total){
8852                     
8853                     this.footer.ds.load({
8854                         params : {
8855                             start : this.footer.cursor + this.footer.pageSize,
8856                             limit : this.footer.pageSize
8857                         },
8858                         add : true
8859                     });
8860                 }
8861             }
8862             
8863         }
8864     },
8865     
8866     onHeaderChange : function()
8867     {
8868         var header = this.renderHeader();
8869         var table = this.el.select('table', true).first();
8870         
8871         this.mainHead.remove();
8872         this.mainHead = table.createChild(header, this.mainBody, false);
8873     },
8874     
8875     onHiddenChange : function(colModel, colIndex, hidden)
8876     {
8877         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8878         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8879         
8880         this.CSS.updateRule(thSelector, "display", "");
8881         this.CSS.updateRule(tdSelector, "display", "");
8882         
8883         if(hidden){
8884             this.CSS.updateRule(thSelector, "display", "none");
8885             this.CSS.updateRule(tdSelector, "display", "none");
8886         }
8887         
8888         this.onHeaderChange();
8889         this.onLoad();
8890     },
8891     
8892     setColumnWidth: function(col_index, width)
8893     {
8894         // width = "md-2 xs-2..."
8895         if(!this.colModel.config[col_index]) {
8896             return;
8897         }
8898         
8899         var w = width.split(" ");
8900         
8901         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8902         
8903         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8904         
8905         
8906         for(var j = 0; j < w.length; j++) {
8907             
8908             if(!w[j]) {
8909                 continue;
8910             }
8911             
8912             var size_cls = w[j].split("-");
8913             
8914             if(!Number.isInteger(size_cls[1] * 1)) {
8915                 continue;
8916             }
8917             
8918             if(!this.colModel.config[col_index][size_cls[0]]) {
8919                 continue;
8920             }
8921             
8922             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8923                 continue;
8924             }
8925             
8926             h_row[0].classList.replace(
8927                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8928                 "col-"+size_cls[0]+"-"+size_cls[1]
8929             );
8930             
8931             for(var i = 0; i < rows.length; i++) {
8932                 
8933                 var size_cls = w[j].split("-");
8934                 
8935                 if(!Number.isInteger(size_cls[1] * 1)) {
8936                     continue;
8937                 }
8938                 
8939                 if(!this.colModel.config[col_index][size_cls[0]]) {
8940                     continue;
8941                 }
8942                 
8943                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8944                     continue;
8945                 }
8946                 
8947                 rows[i].classList.replace(
8948                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8949                     "col-"+size_cls[0]+"-"+size_cls[1]
8950                 );
8951             }
8952             
8953             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8954         }
8955     }
8956 });
8957
8958  
8959
8960  /*
8961  * - LGPL
8962  *
8963  * table cell
8964  * 
8965  */
8966
8967 /**
8968  * @class Roo.bootstrap.TableCell
8969  * @extends Roo.bootstrap.Component
8970  * Bootstrap TableCell class
8971  * @cfg {String} html cell contain text
8972  * @cfg {String} cls cell class
8973  * @cfg {String} tag cell tag (td|th) default td
8974  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8975  * @cfg {String} align Aligns the content in a cell
8976  * @cfg {String} axis Categorizes cells
8977  * @cfg {String} bgcolor Specifies the background color of a cell
8978  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8979  * @cfg {Number} colspan Specifies the number of columns a cell should span
8980  * @cfg {String} headers Specifies one or more header cells a cell is related to
8981  * @cfg {Number} height Sets the height of a cell
8982  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8983  * @cfg {Number} rowspan Sets the number of rows a cell should span
8984  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8985  * @cfg {String} valign Vertical aligns the content in a cell
8986  * @cfg {Number} width Specifies the width of a cell
8987  * 
8988  * @constructor
8989  * Create a new TableCell
8990  * @param {Object} config The config object
8991  */
8992
8993 Roo.bootstrap.TableCell = function(config){
8994     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8995 };
8996
8997 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8998     
8999     html: false,
9000     cls: false,
9001     tag: false,
9002     abbr: false,
9003     align: false,
9004     axis: false,
9005     bgcolor: false,
9006     charoff: false,
9007     colspan: false,
9008     headers: false,
9009     height: false,
9010     nowrap: false,
9011     rowspan: false,
9012     scope: false,
9013     valign: false,
9014     width: false,
9015     
9016     
9017     getAutoCreate : function(){
9018         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9019         
9020         cfg = {
9021             tag: 'td'
9022         };
9023         
9024         if(this.tag){
9025             cfg.tag = this.tag;
9026         }
9027         
9028         if (this.html) {
9029             cfg.html=this.html
9030         }
9031         if (this.cls) {
9032             cfg.cls=this.cls
9033         }
9034         if (this.abbr) {
9035             cfg.abbr=this.abbr
9036         }
9037         if (this.align) {
9038             cfg.align=this.align
9039         }
9040         if (this.axis) {
9041             cfg.axis=this.axis
9042         }
9043         if (this.bgcolor) {
9044             cfg.bgcolor=this.bgcolor
9045         }
9046         if (this.charoff) {
9047             cfg.charoff=this.charoff
9048         }
9049         if (this.colspan) {
9050             cfg.colspan=this.colspan
9051         }
9052         if (this.headers) {
9053             cfg.headers=this.headers
9054         }
9055         if (this.height) {
9056             cfg.height=this.height
9057         }
9058         if (this.nowrap) {
9059             cfg.nowrap=this.nowrap
9060         }
9061         if (this.rowspan) {
9062             cfg.rowspan=this.rowspan
9063         }
9064         if (this.scope) {
9065             cfg.scope=this.scope
9066         }
9067         if (this.valign) {
9068             cfg.valign=this.valign
9069         }
9070         if (this.width) {
9071             cfg.width=this.width
9072         }
9073         
9074         
9075         return cfg;
9076     }
9077    
9078 });
9079
9080  
9081
9082  /*
9083  * - LGPL
9084  *
9085  * table row
9086  * 
9087  */
9088
9089 /**
9090  * @class Roo.bootstrap.TableRow
9091  * @extends Roo.bootstrap.Component
9092  * Bootstrap TableRow class
9093  * @cfg {String} cls row class
9094  * @cfg {String} align Aligns the content in a table row
9095  * @cfg {String} bgcolor Specifies a background color for a table row
9096  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9097  * @cfg {String} valign Vertical aligns the content in a table row
9098  * 
9099  * @constructor
9100  * Create a new TableRow
9101  * @param {Object} config The config object
9102  */
9103
9104 Roo.bootstrap.TableRow = function(config){
9105     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9106 };
9107
9108 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9109     
9110     cls: false,
9111     align: false,
9112     bgcolor: false,
9113     charoff: false,
9114     valign: false,
9115     
9116     getAutoCreate : function(){
9117         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9118         
9119         cfg = {
9120             tag: 'tr'
9121         };
9122             
9123         if(this.cls){
9124             cfg.cls = this.cls;
9125         }
9126         if(this.align){
9127             cfg.align = this.align;
9128         }
9129         if(this.bgcolor){
9130             cfg.bgcolor = this.bgcolor;
9131         }
9132         if(this.charoff){
9133             cfg.charoff = this.charoff;
9134         }
9135         if(this.valign){
9136             cfg.valign = this.valign;
9137         }
9138         
9139         return cfg;
9140     }
9141    
9142 });
9143
9144  
9145
9146  /*
9147  * - LGPL
9148  *
9149  * table body
9150  * 
9151  */
9152
9153 /**
9154  * @class Roo.bootstrap.TableBody
9155  * @extends Roo.bootstrap.Component
9156  * Bootstrap TableBody class
9157  * @cfg {String} cls element class
9158  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9159  * @cfg {String} align Aligns the content inside the element
9160  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9161  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9162  * 
9163  * @constructor
9164  * Create a new TableBody
9165  * @param {Object} config The config object
9166  */
9167
9168 Roo.bootstrap.TableBody = function(config){
9169     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9170 };
9171
9172 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9173     
9174     cls: false,
9175     tag: false,
9176     align: false,
9177     charoff: false,
9178     valign: false,
9179     
9180     getAutoCreate : function(){
9181         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9182         
9183         cfg = {
9184             tag: 'tbody'
9185         };
9186             
9187         if (this.cls) {
9188             cfg.cls=this.cls
9189         }
9190         if(this.tag){
9191             cfg.tag = this.tag;
9192         }
9193         
9194         if(this.align){
9195             cfg.align = this.align;
9196         }
9197         if(this.charoff){
9198             cfg.charoff = this.charoff;
9199         }
9200         if(this.valign){
9201             cfg.valign = this.valign;
9202         }
9203         
9204         return cfg;
9205     }
9206     
9207     
9208 //    initEvents : function()
9209 //    {
9210 //        
9211 //        if(!this.store){
9212 //            return;
9213 //        }
9214 //        
9215 //        this.store = Roo.factory(this.store, Roo.data);
9216 //        this.store.on('load', this.onLoad, this);
9217 //        
9218 //        this.store.load();
9219 //        
9220 //    },
9221 //    
9222 //    onLoad: function () 
9223 //    {   
9224 //        this.fireEvent('load', this);
9225 //    }
9226 //    
9227 //   
9228 });
9229
9230  
9231
9232  /*
9233  * Based on:
9234  * Ext JS Library 1.1.1
9235  * Copyright(c) 2006-2007, Ext JS, LLC.
9236  *
9237  * Originally Released Under LGPL - original licence link has changed is not relivant.
9238  *
9239  * Fork - LGPL
9240  * <script type="text/javascript">
9241  */
9242
9243 // as we use this in bootstrap.
9244 Roo.namespace('Roo.form');
9245  /**
9246  * @class Roo.form.Action
9247  * Internal Class used to handle form actions
9248  * @constructor
9249  * @param {Roo.form.BasicForm} el The form element or its id
9250  * @param {Object} config Configuration options
9251  */
9252
9253  
9254  
9255 // define the action interface
9256 Roo.form.Action = function(form, options){
9257     this.form = form;
9258     this.options = options || {};
9259 };
9260 /**
9261  * Client Validation Failed
9262  * @const 
9263  */
9264 Roo.form.Action.CLIENT_INVALID = 'client';
9265 /**
9266  * Server Validation Failed
9267  * @const 
9268  */
9269 Roo.form.Action.SERVER_INVALID = 'server';
9270  /**
9271  * Connect to Server Failed
9272  * @const 
9273  */
9274 Roo.form.Action.CONNECT_FAILURE = 'connect';
9275 /**
9276  * Reading Data from Server Failed
9277  * @const 
9278  */
9279 Roo.form.Action.LOAD_FAILURE = 'load';
9280
9281 Roo.form.Action.prototype = {
9282     type : 'default',
9283     failureType : undefined,
9284     response : undefined,
9285     result : undefined,
9286
9287     // interface method
9288     run : function(options){
9289
9290     },
9291
9292     // interface method
9293     success : function(response){
9294
9295     },
9296
9297     // interface method
9298     handleResponse : function(response){
9299
9300     },
9301
9302     // default connection failure
9303     failure : function(response){
9304         
9305         this.response = response;
9306         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9307         this.form.afterAction(this, false);
9308     },
9309
9310     processResponse : function(response){
9311         this.response = response;
9312         if(!response.responseText){
9313             return true;
9314         }
9315         this.result = this.handleResponse(response);
9316         return this.result;
9317     },
9318
9319     // utility functions used internally
9320     getUrl : function(appendParams){
9321         var url = this.options.url || this.form.url || this.form.el.dom.action;
9322         if(appendParams){
9323             var p = this.getParams();
9324             if(p){
9325                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9326             }
9327         }
9328         return url;
9329     },
9330
9331     getMethod : function(){
9332         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9333     },
9334
9335     getParams : function(){
9336         var bp = this.form.baseParams;
9337         var p = this.options.params;
9338         if(p){
9339             if(typeof p == "object"){
9340                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9341             }else if(typeof p == 'string' && bp){
9342                 p += '&' + Roo.urlEncode(bp);
9343             }
9344         }else if(bp){
9345             p = Roo.urlEncode(bp);
9346         }
9347         return p;
9348     },
9349
9350     createCallback : function(){
9351         return {
9352             success: this.success,
9353             failure: this.failure,
9354             scope: this,
9355             timeout: (this.form.timeout*1000),
9356             upload: this.form.fileUpload ? this.success : undefined
9357         };
9358     }
9359 };
9360
9361 Roo.form.Action.Submit = function(form, options){
9362     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9363 };
9364
9365 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9366     type : 'submit',
9367
9368     haveProgress : false,
9369     uploadComplete : false,
9370     
9371     // uploadProgress indicator.
9372     uploadProgress : function()
9373     {
9374         if (!this.form.progressUrl) {
9375             return;
9376         }
9377         
9378         if (!this.haveProgress) {
9379             Roo.MessageBox.progress("Uploading", "Uploading");
9380         }
9381         if (this.uploadComplete) {
9382            Roo.MessageBox.hide();
9383            return;
9384         }
9385         
9386         this.haveProgress = true;
9387    
9388         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9389         
9390         var c = new Roo.data.Connection();
9391         c.request({
9392             url : this.form.progressUrl,
9393             params: {
9394                 id : uid
9395             },
9396             method: 'GET',
9397             success : function(req){
9398                //console.log(data);
9399                 var rdata = false;
9400                 var edata;
9401                 try  {
9402                    rdata = Roo.decode(req.responseText)
9403                 } catch (e) {
9404                     Roo.log("Invalid data from server..");
9405                     Roo.log(edata);
9406                     return;
9407                 }
9408                 if (!rdata || !rdata.success) {
9409                     Roo.log(rdata);
9410                     Roo.MessageBox.alert(Roo.encode(rdata));
9411                     return;
9412                 }
9413                 var data = rdata.data;
9414                 
9415                 if (this.uploadComplete) {
9416                    Roo.MessageBox.hide();
9417                    return;
9418                 }
9419                    
9420                 if (data){
9421                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9422                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9423                     );
9424                 }
9425                 this.uploadProgress.defer(2000,this);
9426             },
9427        
9428             failure: function(data) {
9429                 Roo.log('progress url failed ');
9430                 Roo.log(data);
9431             },
9432             scope : this
9433         });
9434            
9435     },
9436     
9437     
9438     run : function()
9439     {
9440         // run get Values on the form, so it syncs any secondary forms.
9441         this.form.getValues();
9442         
9443         var o = this.options;
9444         var method = this.getMethod();
9445         var isPost = method == 'POST';
9446         if(o.clientValidation === false || this.form.isValid()){
9447             
9448             if (this.form.progressUrl) {
9449                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9450                     (new Date() * 1) + '' + Math.random());
9451                     
9452             } 
9453             
9454             
9455             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9456                 form:this.form.el.dom,
9457                 url:this.getUrl(!isPost),
9458                 method: method,
9459                 params:isPost ? this.getParams() : null,
9460                 isUpload: this.form.fileUpload,
9461                 formData : this.form.formData
9462             }));
9463             
9464             this.uploadProgress();
9465
9466         }else if (o.clientValidation !== false){ // client validation failed
9467             this.failureType = Roo.form.Action.CLIENT_INVALID;
9468             this.form.afterAction(this, false);
9469         }
9470     },
9471
9472     success : function(response)
9473     {
9474         this.uploadComplete= true;
9475         if (this.haveProgress) {
9476             Roo.MessageBox.hide();
9477         }
9478         
9479         
9480         var result = this.processResponse(response);
9481         if(result === true || result.success){
9482             this.form.afterAction(this, true);
9483             return;
9484         }
9485         if(result.errors){
9486             this.form.markInvalid(result.errors);
9487             this.failureType = Roo.form.Action.SERVER_INVALID;
9488         }
9489         this.form.afterAction(this, false);
9490     },
9491     failure : function(response)
9492     {
9493         this.uploadComplete= true;
9494         if (this.haveProgress) {
9495             Roo.MessageBox.hide();
9496         }
9497         
9498         this.response = response;
9499         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9500         this.form.afterAction(this, false);
9501     },
9502     
9503     handleResponse : function(response){
9504         if(this.form.errorReader){
9505             var rs = this.form.errorReader.read(response);
9506             var errors = [];
9507             if(rs.records){
9508                 for(var i = 0, len = rs.records.length; i < len; i++) {
9509                     var r = rs.records[i];
9510                     errors[i] = r.data;
9511                 }
9512             }
9513             if(errors.length < 1){
9514                 errors = null;
9515             }
9516             return {
9517                 success : rs.success,
9518                 errors : errors
9519             };
9520         }
9521         var ret = false;
9522         try {
9523             ret = Roo.decode(response.responseText);
9524         } catch (e) {
9525             ret = {
9526                 success: false,
9527                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9528                 errors : []
9529             };
9530         }
9531         return ret;
9532         
9533     }
9534 });
9535
9536
9537 Roo.form.Action.Load = function(form, options){
9538     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9539     this.reader = this.form.reader;
9540 };
9541
9542 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9543     type : 'load',
9544
9545     run : function(){
9546         
9547         Roo.Ajax.request(Roo.apply(
9548                 this.createCallback(), {
9549                     method:this.getMethod(),
9550                     url:this.getUrl(false),
9551                     params:this.getParams()
9552         }));
9553     },
9554
9555     success : function(response){
9556         
9557         var result = this.processResponse(response);
9558         if(result === true || !result.success || !result.data){
9559             this.failureType = Roo.form.Action.LOAD_FAILURE;
9560             this.form.afterAction(this, false);
9561             return;
9562         }
9563         this.form.clearInvalid();
9564         this.form.setValues(result.data);
9565         this.form.afterAction(this, true);
9566     },
9567
9568     handleResponse : function(response){
9569         if(this.form.reader){
9570             var rs = this.form.reader.read(response);
9571             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9572             return {
9573                 success : rs.success,
9574                 data : data
9575             };
9576         }
9577         return Roo.decode(response.responseText);
9578     }
9579 });
9580
9581 Roo.form.Action.ACTION_TYPES = {
9582     'load' : Roo.form.Action.Load,
9583     'submit' : Roo.form.Action.Submit
9584 };/*
9585  * - LGPL
9586  *
9587  * form
9588  *
9589  */
9590
9591 /**
9592  * @class Roo.bootstrap.Form
9593  * @extends Roo.bootstrap.Component
9594  * Bootstrap Form class
9595  * @cfg {String} method  GET | POST (default POST)
9596  * @cfg {String} labelAlign top | left (default top)
9597  * @cfg {String} align left  | right - for navbars
9598  * @cfg {Boolean} loadMask load mask when submit (default true)
9599
9600  *
9601  * @constructor
9602  * Create a new Form
9603  * @param {Object} config The config object
9604  */
9605
9606
9607 Roo.bootstrap.Form = function(config){
9608     
9609     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9610     
9611     Roo.bootstrap.Form.popover.apply();
9612     
9613     this.addEvents({
9614         /**
9615          * @event clientvalidation
9616          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9617          * @param {Form} this
9618          * @param {Boolean} valid true if the form has passed client-side validation
9619          */
9620         clientvalidation: true,
9621         /**
9622          * @event beforeaction
9623          * Fires before any action is performed. Return false to cancel the action.
9624          * @param {Form} this
9625          * @param {Action} action The action to be performed
9626          */
9627         beforeaction: true,
9628         /**
9629          * @event actionfailed
9630          * Fires when an action fails.
9631          * @param {Form} this
9632          * @param {Action} action The action that failed
9633          */
9634         actionfailed : true,
9635         /**
9636          * @event actioncomplete
9637          * Fires when an action is completed.
9638          * @param {Form} this
9639          * @param {Action} action The action that completed
9640          */
9641         actioncomplete : true
9642     });
9643 };
9644
9645 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9646
9647      /**
9648      * @cfg {String} method
9649      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9650      */
9651     method : 'POST',
9652     /**
9653      * @cfg {String} url
9654      * The URL to use for form actions if one isn't supplied in the action options.
9655      */
9656     /**
9657      * @cfg {Boolean} fileUpload
9658      * Set to true if this form is a file upload.
9659      */
9660
9661     /**
9662      * @cfg {Object} baseParams
9663      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9664      */
9665
9666     /**
9667      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9668      */
9669     timeout: 30,
9670     /**
9671      * @cfg {Sting} align (left|right) for navbar forms
9672      */
9673     align : 'left',
9674
9675     // private
9676     activeAction : null,
9677
9678     /**
9679      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9680      * element by passing it or its id or mask the form itself by passing in true.
9681      * @type Mixed
9682      */
9683     waitMsgTarget : false,
9684
9685     loadMask : true,
9686     
9687     /**
9688      * @cfg {Boolean} errorMask (true|false) default false
9689      */
9690     errorMask : false,
9691     
9692     /**
9693      * @cfg {Number} maskOffset Default 100
9694      */
9695     maskOffset : 100,
9696     
9697     /**
9698      * @cfg {Boolean} maskBody
9699      */
9700     maskBody : false,
9701
9702     getAutoCreate : function(){
9703
9704         var cfg = {
9705             tag: 'form',
9706             method : this.method || 'POST',
9707             id : this.id || Roo.id(),
9708             cls : ''
9709         };
9710         if (this.parent().xtype.match(/^Nav/)) {
9711             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9712
9713         }
9714
9715         if (this.labelAlign == 'left' ) {
9716             cfg.cls += ' form-horizontal';
9717         }
9718
9719
9720         return cfg;
9721     },
9722     initEvents : function()
9723     {
9724         this.el.on('submit', this.onSubmit, this);
9725         // this was added as random key presses on the form where triggering form submit.
9726         this.el.on('keypress', function(e) {
9727             if (e.getCharCode() != 13) {
9728                 return true;
9729             }
9730             // we might need to allow it for textareas.. and some other items.
9731             // check e.getTarget().
9732
9733             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9734                 return true;
9735             }
9736
9737             Roo.log("keypress blocked");
9738
9739             e.preventDefault();
9740             return false;
9741         });
9742         
9743     },
9744     // private
9745     onSubmit : function(e){
9746         e.stopEvent();
9747     },
9748
9749      /**
9750      * Returns true if client-side validation on the form is successful.
9751      * @return Boolean
9752      */
9753     isValid : function(){
9754         var items = this.getItems();
9755         var valid = true;
9756         var target = false;
9757         
9758         items.each(function(f){
9759             
9760             if(f.validate()){
9761                 return;
9762             }
9763             
9764             Roo.log('invalid field: ' + f.name);
9765             
9766             valid = false;
9767
9768             if(!target && f.el.isVisible(true)){
9769                 target = f;
9770             }
9771            
9772         });
9773         
9774         if(this.errorMask && !valid){
9775             Roo.bootstrap.Form.popover.mask(this, target);
9776         }
9777         
9778         return valid;
9779     },
9780     
9781     /**
9782      * Returns true if any fields in this form have changed since their original load.
9783      * @return Boolean
9784      */
9785     isDirty : function(){
9786         var dirty = false;
9787         var items = this.getItems();
9788         items.each(function(f){
9789            if(f.isDirty()){
9790                dirty = true;
9791                return false;
9792            }
9793            return true;
9794         });
9795         return dirty;
9796     },
9797      /**
9798      * Performs a predefined action (submit or load) or custom actions you define on this form.
9799      * @param {String} actionName The name of the action type
9800      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9801      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9802      * accept other config options):
9803      * <pre>
9804 Property          Type             Description
9805 ----------------  ---------------  ----------------------------------------------------------------------------------
9806 url               String           The url for the action (defaults to the form's url)
9807 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9808 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9809 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9810                                    validate the form on the client (defaults to false)
9811      * </pre>
9812      * @return {BasicForm} this
9813      */
9814     doAction : function(action, options){
9815         if(typeof action == 'string'){
9816             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9817         }
9818         if(this.fireEvent('beforeaction', this, action) !== false){
9819             this.beforeAction(action);
9820             action.run.defer(100, action);
9821         }
9822         return this;
9823     },
9824
9825     // private
9826     beforeAction : function(action){
9827         var o = action.options;
9828         
9829         if(this.loadMask){
9830             
9831             if(this.maskBody){
9832                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9833             } else {
9834                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9835             }
9836         }
9837         // not really supported yet.. ??
9838
9839         //if(this.waitMsgTarget === true){
9840         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9841         //}else if(this.waitMsgTarget){
9842         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9843         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9844         //}else {
9845         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9846        // }
9847
9848     },
9849
9850     // private
9851     afterAction : function(action, success){
9852         this.activeAction = null;
9853         var o = action.options;
9854
9855         if(this.loadMask){
9856             
9857             if(this.maskBody){
9858                 Roo.get(document.body).unmask();
9859             } else {
9860                 this.el.unmask();
9861             }
9862         }
9863         
9864         //if(this.waitMsgTarget === true){
9865 //            this.el.unmask();
9866         //}else if(this.waitMsgTarget){
9867         //    this.waitMsgTarget.unmask();
9868         //}else{
9869         //    Roo.MessageBox.updateProgress(1);
9870         //    Roo.MessageBox.hide();
9871        // }
9872         //
9873         if(success){
9874             if(o.reset){
9875                 this.reset();
9876             }
9877             Roo.callback(o.success, o.scope, [this, action]);
9878             this.fireEvent('actioncomplete', this, action);
9879
9880         }else{
9881
9882             // failure condition..
9883             // we have a scenario where updates need confirming.
9884             // eg. if a locking scenario exists..
9885             // we look for { errors : { needs_confirm : true }} in the response.
9886             if (
9887                 (typeof(action.result) != 'undefined')  &&
9888                 (typeof(action.result.errors) != 'undefined')  &&
9889                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9890            ){
9891                 var _t = this;
9892                 Roo.log("not supported yet");
9893                  /*
9894
9895                 Roo.MessageBox.confirm(
9896                     "Change requires confirmation",
9897                     action.result.errorMsg,
9898                     function(r) {
9899                         if (r != 'yes') {
9900                             return;
9901                         }
9902                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9903                     }
9904
9905                 );
9906                 */
9907
9908
9909                 return;
9910             }
9911
9912             Roo.callback(o.failure, o.scope, [this, action]);
9913             // show an error message if no failed handler is set..
9914             if (!this.hasListener('actionfailed')) {
9915                 Roo.log("need to add dialog support");
9916                 /*
9917                 Roo.MessageBox.alert("Error",
9918                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9919                         action.result.errorMsg :
9920                         "Saving Failed, please check your entries or try again"
9921                 );
9922                 */
9923             }
9924
9925             this.fireEvent('actionfailed', this, action);
9926         }
9927
9928     },
9929     /**
9930      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9931      * @param {String} id The value to search for
9932      * @return Field
9933      */
9934     findField : function(id){
9935         var items = this.getItems();
9936         var field = items.get(id);
9937         if(!field){
9938              items.each(function(f){
9939                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9940                     field = f;
9941                     return false;
9942                 }
9943                 return true;
9944             });
9945         }
9946         return field || null;
9947     },
9948      /**
9949      * Mark fields in this form invalid in bulk.
9950      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9951      * @return {BasicForm} this
9952      */
9953     markInvalid : function(errors){
9954         if(errors instanceof Array){
9955             for(var i = 0, len = errors.length; i < len; i++){
9956                 var fieldError = errors[i];
9957                 var f = this.findField(fieldError.id);
9958                 if(f){
9959                     f.markInvalid(fieldError.msg);
9960                 }
9961             }
9962         }else{
9963             var field, id;
9964             for(id in errors){
9965                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9966                     field.markInvalid(errors[id]);
9967                 }
9968             }
9969         }
9970         //Roo.each(this.childForms || [], function (f) {
9971         //    f.markInvalid(errors);
9972         //});
9973
9974         return this;
9975     },
9976
9977     /**
9978      * Set values for fields in this form in bulk.
9979      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9980      * @return {BasicForm} this
9981      */
9982     setValues : function(values){
9983         if(values instanceof Array){ // array of objects
9984             for(var i = 0, len = values.length; i < len; i++){
9985                 var v = values[i];
9986                 var f = this.findField(v.id);
9987                 if(f){
9988                     f.setValue(v.value);
9989                     if(this.trackResetOnLoad){
9990                         f.originalValue = f.getValue();
9991                     }
9992                 }
9993             }
9994         }else{ // object hash
9995             var field, id;
9996             for(id in values){
9997                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9998
9999                     if (field.setFromData &&
10000                         field.valueField &&
10001                         field.displayField &&
10002                         // combos' with local stores can
10003                         // be queried via setValue()
10004                         // to set their value..
10005                         (field.store && !field.store.isLocal)
10006                         ) {
10007                         // it's a combo
10008                         var sd = { };
10009                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10010                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10011                         field.setFromData(sd);
10012
10013                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10014                         
10015                         field.setFromData(values);
10016                         
10017                     } else {
10018                         field.setValue(values[id]);
10019                     }
10020
10021
10022                     if(this.trackResetOnLoad){
10023                         field.originalValue = field.getValue();
10024                     }
10025                 }
10026             }
10027         }
10028
10029         //Roo.each(this.childForms || [], function (f) {
10030         //    f.setValues(values);
10031         //});
10032
10033         return this;
10034     },
10035
10036     /**
10037      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10038      * they are returned as an array.
10039      * @param {Boolean} asString
10040      * @return {Object}
10041      */
10042     getValues : function(asString){
10043         //if (this.childForms) {
10044             // copy values from the child forms
10045         //    Roo.each(this.childForms, function (f) {
10046         //        this.setValues(f.getValues());
10047         //    }, this);
10048         //}
10049
10050
10051
10052         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10053         if(asString === true){
10054             return fs;
10055         }
10056         return Roo.urlDecode(fs);
10057     },
10058
10059     /**
10060      * Returns the fields in this form as an object with key/value pairs.
10061      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10062      * @return {Object}
10063      */
10064     getFieldValues : function(with_hidden)
10065     {
10066         var items = this.getItems();
10067         var ret = {};
10068         items.each(function(f){
10069             
10070             if (!f.getName()) {
10071                 return;
10072             }
10073             
10074             var v = f.getValue();
10075             
10076             if (f.inputType =='radio') {
10077                 if (typeof(ret[f.getName()]) == 'undefined') {
10078                     ret[f.getName()] = ''; // empty..
10079                 }
10080
10081                 if (!f.el.dom.checked) {
10082                     return;
10083
10084                 }
10085                 v = f.el.dom.value;
10086
10087             }
10088             
10089             if(f.xtype == 'MoneyField'){
10090                 ret[f.currencyName] = f.getCurrency();
10091             }
10092
10093             // not sure if this supported any more..
10094             if ((typeof(v) == 'object') && f.getRawValue) {
10095                 v = f.getRawValue() ; // dates..
10096             }
10097             // combo boxes where name != hiddenName...
10098             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10099                 ret[f.name] = f.getRawValue();
10100             }
10101             ret[f.getName()] = v;
10102         });
10103
10104         return ret;
10105     },
10106
10107     /**
10108      * Clears all invalid messages in this form.
10109      * @return {BasicForm} this
10110      */
10111     clearInvalid : function(){
10112         var items = this.getItems();
10113
10114         items.each(function(f){
10115            f.clearInvalid();
10116         });
10117
10118         return this;
10119     },
10120
10121     /**
10122      * Resets this form.
10123      * @return {BasicForm} this
10124      */
10125     reset : function(){
10126         var items = this.getItems();
10127         items.each(function(f){
10128             f.reset();
10129         });
10130
10131         Roo.each(this.childForms || [], function (f) {
10132             f.reset();
10133         });
10134
10135
10136         return this;
10137     },
10138     
10139     getItems : function()
10140     {
10141         var r=new Roo.util.MixedCollection(false, function(o){
10142             return o.id || (o.id = Roo.id());
10143         });
10144         var iter = function(el) {
10145             if (el.inputEl) {
10146                 r.add(el);
10147             }
10148             if (!el.items) {
10149                 return;
10150             }
10151             Roo.each(el.items,function(e) {
10152                 iter(e);
10153             });
10154         };
10155
10156         iter(this);
10157         return r;
10158     },
10159     
10160     hideFields : function(items)
10161     {
10162         Roo.each(items, function(i){
10163             
10164             var f = this.findField(i);
10165             
10166             if(!f){
10167                 return;
10168             }
10169             
10170             f.hide();
10171             
10172         }, this);
10173     },
10174     
10175     showFields : function(items)
10176     {
10177         Roo.each(items, function(i){
10178             
10179             var f = this.findField(i);
10180             
10181             if(!f){
10182                 return;
10183             }
10184             
10185             f.show();
10186             
10187         }, this);
10188     }
10189
10190 });
10191
10192 Roo.apply(Roo.bootstrap.Form, {
10193     
10194     popover : {
10195         
10196         padding : 5,
10197         
10198         isApplied : false,
10199         
10200         isMasked : false,
10201         
10202         form : false,
10203         
10204         target : false,
10205         
10206         toolTip : false,
10207         
10208         intervalID : false,
10209         
10210         maskEl : false,
10211         
10212         apply : function()
10213         {
10214             if(this.isApplied){
10215                 return;
10216             }
10217             
10218             this.maskEl = {
10219                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10220                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10221                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10222                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10223             };
10224             
10225             this.maskEl.top.enableDisplayMode("block");
10226             this.maskEl.left.enableDisplayMode("block");
10227             this.maskEl.bottom.enableDisplayMode("block");
10228             this.maskEl.right.enableDisplayMode("block");
10229             
10230             this.toolTip = new Roo.bootstrap.Tooltip({
10231                 cls : 'roo-form-error-popover',
10232                 alignment : {
10233                     'left' : ['r-l', [-2,0], 'right'],
10234                     'right' : ['l-r', [2,0], 'left'],
10235                     'bottom' : ['tl-bl', [0,2], 'top'],
10236                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10237                 }
10238             });
10239             
10240             this.toolTip.render(Roo.get(document.body));
10241
10242             this.toolTip.el.enableDisplayMode("block");
10243             
10244             Roo.get(document.body).on('click', function(){
10245                 this.unmask();
10246             }, this);
10247             
10248             Roo.get(document.body).on('touchstart', function(){
10249                 this.unmask();
10250             }, this);
10251             
10252             this.isApplied = true
10253         },
10254         
10255         mask : function(form, target)
10256         {
10257             this.form = form;
10258             
10259             this.target = target;
10260             
10261             if(!this.form.errorMask || !target.el){
10262                 return;
10263             }
10264             
10265             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10266             
10267             Roo.log(scrollable);
10268             
10269             var ot = this.target.el.calcOffsetsTo(scrollable);
10270             
10271             var scrollTo = ot[1] - this.form.maskOffset;
10272             
10273             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10274             
10275             scrollable.scrollTo('top', scrollTo);
10276             
10277             var box = this.target.el.getBox();
10278             Roo.log(box);
10279             var zIndex = Roo.bootstrap.Modal.zIndex++;
10280
10281             
10282             this.maskEl.top.setStyle('position', 'absolute');
10283             this.maskEl.top.setStyle('z-index', zIndex);
10284             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10285             this.maskEl.top.setLeft(0);
10286             this.maskEl.top.setTop(0);
10287             this.maskEl.top.show();
10288             
10289             this.maskEl.left.setStyle('position', 'absolute');
10290             this.maskEl.left.setStyle('z-index', zIndex);
10291             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10292             this.maskEl.left.setLeft(0);
10293             this.maskEl.left.setTop(box.y - this.padding);
10294             this.maskEl.left.show();
10295
10296             this.maskEl.bottom.setStyle('position', 'absolute');
10297             this.maskEl.bottom.setStyle('z-index', zIndex);
10298             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10299             this.maskEl.bottom.setLeft(0);
10300             this.maskEl.bottom.setTop(box.bottom + this.padding);
10301             this.maskEl.bottom.show();
10302
10303             this.maskEl.right.setStyle('position', 'absolute');
10304             this.maskEl.right.setStyle('z-index', zIndex);
10305             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10306             this.maskEl.right.setLeft(box.right + this.padding);
10307             this.maskEl.right.setTop(box.y - this.padding);
10308             this.maskEl.right.show();
10309
10310             this.toolTip.bindEl = this.target.el;
10311
10312             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10313
10314             var tip = this.target.blankText;
10315
10316             if(this.target.getValue() !== '' ) {
10317                 
10318                 if (this.target.invalidText.length) {
10319                     tip = this.target.invalidText;
10320                 } else if (this.target.regexText.length){
10321                     tip = this.target.regexText;
10322                 }
10323             }
10324
10325             this.toolTip.show(tip);
10326
10327             this.intervalID = window.setInterval(function() {
10328                 Roo.bootstrap.Form.popover.unmask();
10329             }, 10000);
10330
10331             window.onwheel = function(){ return false;};
10332             
10333             (function(){ this.isMasked = true; }).defer(500, this);
10334             
10335         },
10336         
10337         unmask : function()
10338         {
10339             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10340                 return;
10341             }
10342             
10343             this.maskEl.top.setStyle('position', 'absolute');
10344             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10345             this.maskEl.top.hide();
10346
10347             this.maskEl.left.setStyle('position', 'absolute');
10348             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10349             this.maskEl.left.hide();
10350
10351             this.maskEl.bottom.setStyle('position', 'absolute');
10352             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10353             this.maskEl.bottom.hide();
10354
10355             this.maskEl.right.setStyle('position', 'absolute');
10356             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10357             this.maskEl.right.hide();
10358             
10359             this.toolTip.hide();
10360             
10361             this.toolTip.el.hide();
10362             
10363             window.onwheel = function(){ return true;};
10364             
10365             if(this.intervalID){
10366                 window.clearInterval(this.intervalID);
10367                 this.intervalID = false;
10368             }
10369             
10370             this.isMasked = false;
10371             
10372         }
10373         
10374     }
10375     
10376 });
10377
10378 /*
10379  * Based on:
10380  * Ext JS Library 1.1.1
10381  * Copyright(c) 2006-2007, Ext JS, LLC.
10382  *
10383  * Originally Released Under LGPL - original licence link has changed is not relivant.
10384  *
10385  * Fork - LGPL
10386  * <script type="text/javascript">
10387  */
10388 /**
10389  * @class Roo.form.VTypes
10390  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10391  * @singleton
10392  */
10393 Roo.form.VTypes = function(){
10394     // closure these in so they are only created once.
10395     var alpha = /^[a-zA-Z_]+$/;
10396     var alphanum = /^[a-zA-Z0-9_]+$/;
10397     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10398     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10399
10400     // All these messages and functions are configurable
10401     return {
10402         /**
10403          * The function used to validate email addresses
10404          * @param {String} value The email address
10405          */
10406         'email' : function(v){
10407             return email.test(v);
10408         },
10409         /**
10410          * The error text to display when the email validation function returns false
10411          * @type String
10412          */
10413         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10414         /**
10415          * The keystroke filter mask to be applied on email input
10416          * @type RegExp
10417          */
10418         'emailMask' : /[a-z0-9_\.\-@]/i,
10419
10420         /**
10421          * The function used to validate URLs
10422          * @param {String} value The URL
10423          */
10424         'url' : function(v){
10425             return url.test(v);
10426         },
10427         /**
10428          * The error text to display when the url validation function returns false
10429          * @type String
10430          */
10431         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10432         
10433         /**
10434          * The function used to validate alpha values
10435          * @param {String} value The value
10436          */
10437         'alpha' : function(v){
10438             return alpha.test(v);
10439         },
10440         /**
10441          * The error text to display when the alpha validation function returns false
10442          * @type String
10443          */
10444         'alphaText' : 'This field should only contain letters and _',
10445         /**
10446          * The keystroke filter mask to be applied on alpha input
10447          * @type RegExp
10448          */
10449         'alphaMask' : /[a-z_]/i,
10450
10451         /**
10452          * The function used to validate alphanumeric values
10453          * @param {String} value The value
10454          */
10455         'alphanum' : function(v){
10456             return alphanum.test(v);
10457         },
10458         /**
10459          * The error text to display when the alphanumeric validation function returns false
10460          * @type String
10461          */
10462         'alphanumText' : 'This field should only contain letters, numbers and _',
10463         /**
10464          * The keystroke filter mask to be applied on alphanumeric input
10465          * @type RegExp
10466          */
10467         'alphanumMask' : /[a-z0-9_]/i
10468     };
10469 }();/*
10470  * - LGPL
10471  *
10472  * Input
10473  * 
10474  */
10475
10476 /**
10477  * @class Roo.bootstrap.Input
10478  * @extends Roo.bootstrap.Component
10479  * Bootstrap Input class
10480  * @cfg {Boolean} disabled is it disabled
10481  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10482  * @cfg {String} name name of the input
10483  * @cfg {string} fieldLabel - the label associated
10484  * @cfg {string} placeholder - placeholder to put in text.
10485  * @cfg {string}  before - input group add on before
10486  * @cfg {string} after - input group add on after
10487  * @cfg {string} size - (lg|sm) or leave empty..
10488  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10489  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10490  * @cfg {Number} md colspan out of 12 for computer-sized screens
10491  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10492  * @cfg {string} value default value of the input
10493  * @cfg {Number} labelWidth set the width of label 
10494  * @cfg {Number} labellg set the width of label (1-12)
10495  * @cfg {Number} labelmd set the width of label (1-12)
10496  * @cfg {Number} labelsm set the width of label (1-12)
10497  * @cfg {Number} labelxs set the width of label (1-12)
10498  * @cfg {String} labelAlign (top|left)
10499  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10500  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10501  * @cfg {String} indicatorpos (left|right) default left
10502  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10503  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10504  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10505
10506  * @cfg {String} align (left|center|right) Default left
10507  * @cfg {Boolean} forceFeedback (true|false) Default false
10508  * 
10509  * @constructor
10510  * Create a new Input
10511  * @param {Object} config The config object
10512  */
10513
10514 Roo.bootstrap.Input = function(config){
10515     
10516     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10517     
10518     this.addEvents({
10519         /**
10520          * @event focus
10521          * Fires when this field receives input focus.
10522          * @param {Roo.form.Field} this
10523          */
10524         focus : true,
10525         /**
10526          * @event blur
10527          * Fires when this field loses input focus.
10528          * @param {Roo.form.Field} this
10529          */
10530         blur : true,
10531         /**
10532          * @event specialkey
10533          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10534          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10535          * @param {Roo.form.Field} this
10536          * @param {Roo.EventObject} e The event object
10537          */
10538         specialkey : true,
10539         /**
10540          * @event change
10541          * Fires just before the field blurs if the field value has changed.
10542          * @param {Roo.form.Field} this
10543          * @param {Mixed} newValue The new value
10544          * @param {Mixed} oldValue The original value
10545          */
10546         change : true,
10547         /**
10548          * @event invalid
10549          * Fires after the field has been marked as invalid.
10550          * @param {Roo.form.Field} this
10551          * @param {String} msg The validation message
10552          */
10553         invalid : true,
10554         /**
10555          * @event valid
10556          * Fires after the field has been validated with no errors.
10557          * @param {Roo.form.Field} this
10558          */
10559         valid : true,
10560          /**
10561          * @event keyup
10562          * Fires after the key up
10563          * @param {Roo.form.Field} this
10564          * @param {Roo.EventObject}  e The event Object
10565          */
10566         keyup : true,
10567         /**
10568          * @event paste
10569          * Fires after the user pastes into input
10570          * @param {Roo.form.Field} this
10571          * @param {Roo.EventObject}  e The event Object
10572          */
10573         paste : true
10574     });
10575 };
10576
10577 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10578      /**
10579      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10580       automatic validation (defaults to "keyup").
10581      */
10582     validationEvent : "keyup",
10583      /**
10584      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10585      */
10586     validateOnBlur : true,
10587     /**
10588      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10589      */
10590     validationDelay : 250,
10591      /**
10592      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10593      */
10594     focusClass : "x-form-focus",  // not needed???
10595     
10596        
10597     /**
10598      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10599      */
10600     invalidClass : "has-warning",
10601     
10602     /**
10603      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10604      */
10605     validClass : "has-success",
10606     
10607     /**
10608      * @cfg {Boolean} hasFeedback (true|false) default true
10609      */
10610     hasFeedback : true,
10611     
10612     /**
10613      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10614      */
10615     invalidFeedbackClass : "glyphicon-warning-sign",
10616     
10617     /**
10618      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10619      */
10620     validFeedbackClass : "glyphicon-ok",
10621     
10622     /**
10623      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10624      */
10625     selectOnFocus : false,
10626     
10627      /**
10628      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10629      */
10630     maskRe : null,
10631        /**
10632      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10633      */
10634     vtype : null,
10635     
10636       /**
10637      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10638      */
10639     disableKeyFilter : false,
10640     
10641        /**
10642      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10643      */
10644     disabled : false,
10645      /**
10646      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10647      */
10648     allowBlank : true,
10649     /**
10650      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10651      */
10652     blankText : "Please complete this mandatory field",
10653     
10654      /**
10655      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10656      */
10657     minLength : 0,
10658     /**
10659      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10660      */
10661     maxLength : Number.MAX_VALUE,
10662     /**
10663      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10664      */
10665     minLengthText : "The minimum length for this field is {0}",
10666     /**
10667      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10668      */
10669     maxLengthText : "The maximum length for this field is {0}",
10670   
10671     
10672     /**
10673      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10674      * If available, this function will be called only after the basic validators all return true, and will be passed the
10675      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10676      */
10677     validator : null,
10678     /**
10679      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10680      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10681      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10682      */
10683     regex : null,
10684     /**
10685      * @cfg {String} regexText -- Depricated - use Invalid Text
10686      */
10687     regexText : "",
10688     
10689     /**
10690      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10691      */
10692     invalidText : "",
10693     
10694     
10695     
10696     autocomplete: false,
10697     
10698     
10699     fieldLabel : '',
10700     inputType : 'text',
10701     
10702     name : false,
10703     placeholder: false,
10704     before : false,
10705     after : false,
10706     size : false,
10707     hasFocus : false,
10708     preventMark: false,
10709     isFormField : true,
10710     value : '',
10711     labelWidth : 2,
10712     labelAlign : false,
10713     readOnly : false,
10714     align : false,
10715     formatedValue : false,
10716     forceFeedback : false,
10717     
10718     indicatorpos : 'left',
10719     
10720     labellg : 0,
10721     labelmd : 0,
10722     labelsm : 0,
10723     labelxs : 0,
10724     
10725     capture : '',
10726     accept : '',
10727     
10728     parentLabelAlign : function()
10729     {
10730         var parent = this;
10731         while (parent.parent()) {
10732             parent = parent.parent();
10733             if (typeof(parent.labelAlign) !='undefined') {
10734                 return parent.labelAlign;
10735             }
10736         }
10737         return 'left';
10738         
10739     },
10740     
10741     getAutoCreate : function()
10742     {
10743         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10744         
10745         var id = Roo.id();
10746         
10747         var cfg = {};
10748         
10749         if(this.inputType != 'hidden'){
10750             cfg.cls = 'form-group' //input-group
10751         }
10752         
10753         var input =  {
10754             tag: 'input',
10755             id : id,
10756             type : this.inputType,
10757             value : this.value,
10758             cls : 'form-control',
10759             placeholder : this.placeholder || '',
10760             autocomplete : this.autocomplete || 'new-password'
10761         };
10762         if (this.inputType == 'file') {
10763             input.style = 'overflow:hidden'; // why not in CSS?
10764         }
10765         
10766         if(this.capture.length){
10767             input.capture = this.capture;
10768         }
10769         
10770         if(this.accept.length){
10771             input.accept = this.accept + "/*";
10772         }
10773         
10774         if(this.align){
10775             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10776         }
10777         
10778         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10779             input.maxLength = this.maxLength;
10780         }
10781         
10782         if (this.disabled) {
10783             input.disabled=true;
10784         }
10785         
10786         if (this.readOnly) {
10787             input.readonly=true;
10788         }
10789         
10790         if (this.name) {
10791             input.name = this.name;
10792         }
10793         
10794         if (this.size) {
10795             input.cls += ' input-' + this.size;
10796         }
10797         
10798         var settings=this;
10799         ['xs','sm','md','lg'].map(function(size){
10800             if (settings[size]) {
10801                 cfg.cls += ' col-' + size + '-' + settings[size];
10802             }
10803         });
10804         
10805         var inputblock = input;
10806         
10807         var feedback = {
10808             tag: 'span',
10809             cls: 'glyphicon form-control-feedback'
10810         };
10811             
10812         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10813             
10814             inputblock = {
10815                 cls : 'has-feedback',
10816                 cn :  [
10817                     input,
10818                     feedback
10819                 ] 
10820             };  
10821         }
10822         
10823         if (this.before || this.after) {
10824             
10825             inputblock = {
10826                 cls : 'input-group',
10827                 cn :  [] 
10828             };
10829             
10830             if (this.before && typeof(this.before) == 'string') {
10831                 
10832                 inputblock.cn.push({
10833                     tag :'span',
10834                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10835                     html : this.before
10836                 });
10837             }
10838             if (this.before && typeof(this.before) == 'object') {
10839                 this.before = Roo.factory(this.before);
10840                 
10841                 inputblock.cn.push({
10842                     tag :'span',
10843                     cls : 'roo-input-before input-group-prepend   input-group-' +
10844                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10845                 });
10846             }
10847             
10848             inputblock.cn.push(input);
10849             
10850             if (this.after && typeof(this.after) == 'string') {
10851                 inputblock.cn.push({
10852                     tag :'span',
10853                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10854                     html : this.after
10855                 });
10856             }
10857             if (this.after && typeof(this.after) == 'object') {
10858                 this.after = Roo.factory(this.after);
10859                 
10860                 inputblock.cn.push({
10861                     tag :'span',
10862                     cls : 'roo-input-after input-group-append  input-group-' +
10863                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10864                 });
10865             }
10866             
10867             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10868                 inputblock.cls += ' has-feedback';
10869                 inputblock.cn.push(feedback);
10870             }
10871         };
10872         var indicator = {
10873             tag : 'i',
10874             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10875             tooltip : 'This field is required'
10876         };
10877         if (this.allowBlank ) {
10878             indicator.style = this.allowBlank ? ' display:none' : '';
10879         }
10880         if (align ==='left' && this.fieldLabel.length) {
10881             
10882             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10883             
10884             cfg.cn = [
10885                 indicator,
10886                 {
10887                     tag: 'label',
10888                     'for' :  id,
10889                     cls : 'control-label col-form-label',
10890                     html : this.fieldLabel
10891
10892                 },
10893                 {
10894                     cls : "", 
10895                     cn: [
10896                         inputblock
10897                     ]
10898                 }
10899             ];
10900             
10901             var labelCfg = cfg.cn[1];
10902             var contentCfg = cfg.cn[2];
10903             
10904             if(this.indicatorpos == 'right'){
10905                 cfg.cn = [
10906                     {
10907                         tag: 'label',
10908                         'for' :  id,
10909                         cls : 'control-label col-form-label',
10910                         cn : [
10911                             {
10912                                 tag : 'span',
10913                                 html : this.fieldLabel
10914                             },
10915                             indicator
10916                         ]
10917                     },
10918                     {
10919                         cls : "",
10920                         cn: [
10921                             inputblock
10922                         ]
10923                     }
10924
10925                 ];
10926                 
10927                 labelCfg = cfg.cn[0];
10928                 contentCfg = cfg.cn[1];
10929             
10930             }
10931             
10932             if(this.labelWidth > 12){
10933                 labelCfg.style = "width: " + this.labelWidth + 'px';
10934             }
10935             
10936             if(this.labelWidth < 13 && this.labelmd == 0){
10937                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
10938             }
10939             
10940             if(this.labellg > 0){
10941                 labelCfg.cls += ' col-lg-' + this.labellg;
10942                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10943             }
10944             
10945             if(this.labelmd > 0){
10946                 labelCfg.cls += ' col-md-' + this.labelmd;
10947                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10948             }
10949             
10950             if(this.labelsm > 0){
10951                 labelCfg.cls += ' col-sm-' + this.labelsm;
10952                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10953             }
10954             
10955             if(this.labelxs > 0){
10956                 labelCfg.cls += ' col-xs-' + this.labelxs;
10957                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10958             }
10959             
10960             
10961         } else if ( this.fieldLabel.length) {
10962                 
10963             
10964             
10965             cfg.cn = [
10966                 {
10967                     tag : 'i',
10968                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10969                     tooltip : 'This field is required',
10970                     style : this.allowBlank ? ' display:none' : '' 
10971                 },
10972                 {
10973                     tag: 'label',
10974                    //cls : 'input-group-addon',
10975                     html : this.fieldLabel
10976
10977                 },
10978
10979                inputblock
10980
10981            ];
10982            
10983            if(this.indicatorpos == 'right'){
10984        
10985                 cfg.cn = [
10986                     {
10987                         tag: 'label',
10988                        //cls : 'input-group-addon',
10989                         html : this.fieldLabel
10990
10991                     },
10992                     {
10993                         tag : 'i',
10994                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10995                         tooltip : 'This field is required',
10996                         style : this.allowBlank ? ' display:none' : '' 
10997                     },
10998
10999                    inputblock
11000
11001                ];
11002
11003             }
11004
11005         } else {
11006             
11007             cfg.cn = [
11008
11009                     inputblock
11010
11011             ];
11012                 
11013                 
11014         };
11015         
11016         if (this.parentType === 'Navbar' &&  this.parent().bar) {
11017            cfg.cls += ' navbar-form';
11018         }
11019         
11020         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11021             // on BS4 we do this only if not form 
11022             cfg.cls += ' navbar-form';
11023             cfg.tag = 'li';
11024         }
11025         
11026         return cfg;
11027         
11028     },
11029     /**
11030      * return the real input element.
11031      */
11032     inputEl: function ()
11033     {
11034         return this.el.select('input.form-control',true).first();
11035     },
11036     
11037     tooltipEl : function()
11038     {
11039         return this.inputEl();
11040     },
11041     
11042     indicatorEl : function()
11043     {
11044         if (Roo.bootstrap.version == 4) {
11045             return false; // not enabled in v4 yet.
11046         }
11047         
11048         var indicator = this.el.select('i.roo-required-indicator',true).first();
11049         
11050         if(!indicator){
11051             return false;
11052         }
11053         
11054         return indicator;
11055         
11056     },
11057     
11058     setDisabled : function(v)
11059     {
11060         var i  = this.inputEl().dom;
11061         if (!v) {
11062             i.removeAttribute('disabled');
11063             return;
11064             
11065         }
11066         i.setAttribute('disabled','true');
11067     },
11068     initEvents : function()
11069     {
11070           
11071         this.inputEl().on("keydown" , this.fireKey,  this);
11072         this.inputEl().on("focus", this.onFocus,  this);
11073         this.inputEl().on("blur", this.onBlur,  this);
11074         
11075         this.inputEl().relayEvent('keyup', this);
11076         this.inputEl().relayEvent('paste', this);
11077         
11078         this.indicator = this.indicatorEl();
11079         
11080         if(this.indicator){
11081             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11082         }
11083  
11084         // reference to original value for reset
11085         this.originalValue = this.getValue();
11086         //Roo.form.TextField.superclass.initEvents.call(this);
11087         if(this.validationEvent == 'keyup'){
11088             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11089             this.inputEl().on('keyup', this.filterValidation, this);
11090         }
11091         else if(this.validationEvent !== false){
11092             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11093         }
11094         
11095         if(this.selectOnFocus){
11096             this.on("focus", this.preFocus, this);
11097             
11098         }
11099         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11100             this.inputEl().on("keypress", this.filterKeys, this);
11101         } else {
11102             this.inputEl().relayEvent('keypress', this);
11103         }
11104        /* if(this.grow){
11105             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11106             this.el.on("click", this.autoSize,  this);
11107         }
11108         */
11109         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11110             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11111         }
11112         
11113         if (typeof(this.before) == 'object') {
11114             this.before.render(this.el.select('.roo-input-before',true).first());
11115         }
11116         if (typeof(this.after) == 'object') {
11117             this.after.render(this.el.select('.roo-input-after',true).first());
11118         }
11119         
11120         this.inputEl().on('change', this.onChange, this);
11121         
11122     },
11123     filterValidation : function(e){
11124         if(!e.isNavKeyPress()){
11125             this.validationTask.delay(this.validationDelay);
11126         }
11127     },
11128      /**
11129      * Validates the field value
11130      * @return {Boolean} True if the value is valid, else false
11131      */
11132     validate : function(){
11133         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11134         if(this.disabled || this.validateValue(this.getRawValue())){
11135             this.markValid();
11136             return true;
11137         }
11138         
11139         this.markInvalid();
11140         return false;
11141     },
11142     
11143     
11144     /**
11145      * Validates a value according to the field's validation rules and marks the field as invalid
11146      * if the validation fails
11147      * @param {Mixed} value The value to validate
11148      * @return {Boolean} True if the value is valid, else false
11149      */
11150     validateValue : function(value)
11151     {
11152         if(this.getVisibilityEl().hasClass('hidden')){
11153             return true;
11154         }
11155         
11156         if(value.length < 1)  { // if it's blank
11157             if(this.allowBlank){
11158                 return true;
11159             }
11160             return false;
11161         }
11162         
11163         if(value.length < this.minLength){
11164             return false;
11165         }
11166         if(value.length > this.maxLength){
11167             return false;
11168         }
11169         if(this.vtype){
11170             var vt = Roo.form.VTypes;
11171             if(!vt[this.vtype](value, this)){
11172                 return false;
11173             }
11174         }
11175         if(typeof this.validator == "function"){
11176             var msg = this.validator(value);
11177             if(msg !== true){
11178                 return false;
11179             }
11180             if (typeof(msg) == 'string') {
11181                 this.invalidText = msg;
11182             }
11183         }
11184         
11185         if(this.regex && !this.regex.test(value)){
11186             return false;
11187         }
11188         
11189         return true;
11190     },
11191     
11192      // private
11193     fireKey : function(e){
11194         //Roo.log('field ' + e.getKey());
11195         if(e.isNavKeyPress()){
11196             this.fireEvent("specialkey", this, e);
11197         }
11198     },
11199     focus : function (selectText){
11200         if(this.rendered){
11201             this.inputEl().focus();
11202             if(selectText === true){
11203                 this.inputEl().dom.select();
11204             }
11205         }
11206         return this;
11207     } ,
11208     
11209     onFocus : function(){
11210         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11211            // this.el.addClass(this.focusClass);
11212         }
11213         if(!this.hasFocus){
11214             this.hasFocus = true;
11215             this.startValue = this.getValue();
11216             this.fireEvent("focus", this);
11217         }
11218     },
11219     
11220     beforeBlur : Roo.emptyFn,
11221
11222     
11223     // private
11224     onBlur : function(){
11225         this.beforeBlur();
11226         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11227             //this.el.removeClass(this.focusClass);
11228         }
11229         this.hasFocus = false;
11230         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11231             this.validate();
11232         }
11233         var v = this.getValue();
11234         if(String(v) !== String(this.startValue)){
11235             this.fireEvent('change', this, v, this.startValue);
11236         }
11237         this.fireEvent("blur", this);
11238     },
11239     
11240     onChange : function(e)
11241     {
11242         var v = this.getValue();
11243         if(String(v) !== String(this.startValue)){
11244             this.fireEvent('change', this, v, this.startValue);
11245         }
11246         
11247     },
11248     
11249     /**
11250      * Resets the current field value to the originally loaded value and clears any validation messages
11251      */
11252     reset : function(){
11253         this.setValue(this.originalValue);
11254         this.validate();
11255     },
11256      /**
11257      * Returns the name of the field
11258      * @return {Mixed} name The name field
11259      */
11260     getName: function(){
11261         return this.name;
11262     },
11263      /**
11264      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11265      * @return {Mixed} value The field value
11266      */
11267     getValue : function(){
11268         
11269         var v = this.inputEl().getValue();
11270         
11271         return v;
11272     },
11273     /**
11274      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11275      * @return {Mixed} value The field value
11276      */
11277     getRawValue : function(){
11278         var v = this.inputEl().getValue();
11279         
11280         return v;
11281     },
11282     
11283     /**
11284      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11285      * @param {Mixed} value The value to set
11286      */
11287     setRawValue : function(v){
11288         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11289     },
11290     
11291     selectText : function(start, end){
11292         var v = this.getRawValue();
11293         if(v.length > 0){
11294             start = start === undefined ? 0 : start;
11295             end = end === undefined ? v.length : end;
11296             var d = this.inputEl().dom;
11297             if(d.setSelectionRange){
11298                 d.setSelectionRange(start, end);
11299             }else if(d.createTextRange){
11300                 var range = d.createTextRange();
11301                 range.moveStart("character", start);
11302                 range.moveEnd("character", v.length-end);
11303                 range.select();
11304             }
11305         }
11306     },
11307     
11308     /**
11309      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11310      * @param {Mixed} value The value to set
11311      */
11312     setValue : function(v){
11313         this.value = v;
11314         if(this.rendered){
11315             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11316             this.validate();
11317         }
11318     },
11319     
11320     /*
11321     processValue : function(value){
11322         if(this.stripCharsRe){
11323             var newValue = value.replace(this.stripCharsRe, '');
11324             if(newValue !== value){
11325                 this.setRawValue(newValue);
11326                 return newValue;
11327             }
11328         }
11329         return value;
11330     },
11331   */
11332     preFocus : function(){
11333         
11334         if(this.selectOnFocus){
11335             this.inputEl().dom.select();
11336         }
11337     },
11338     filterKeys : function(e){
11339         var k = e.getKey();
11340         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11341             return;
11342         }
11343         var c = e.getCharCode(), cc = String.fromCharCode(c);
11344         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11345             return;
11346         }
11347         if(!this.maskRe.test(cc)){
11348             e.stopEvent();
11349         }
11350     },
11351      /**
11352      * Clear any invalid styles/messages for this field
11353      */
11354     clearInvalid : function(){
11355         
11356         if(!this.el || this.preventMark){ // not rendered
11357             return;
11358         }
11359         
11360         
11361         this.el.removeClass([this.invalidClass, 'is-invalid']);
11362         
11363         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11364             
11365             var feedback = this.el.select('.form-control-feedback', true).first();
11366             
11367             if(feedback){
11368                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11369             }
11370             
11371         }
11372         
11373         if(this.indicator){
11374             this.indicator.removeClass('visible');
11375             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11376         }
11377         
11378         this.fireEvent('valid', this);
11379     },
11380     
11381      /**
11382      * Mark this field as valid
11383      */
11384     markValid : function()
11385     {
11386         if(!this.el  || this.preventMark){ // not rendered...
11387             return;
11388         }
11389         
11390         this.el.removeClass([this.invalidClass, this.validClass]);
11391         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11392
11393         var feedback = this.el.select('.form-control-feedback', true).first();
11394             
11395         if(feedback){
11396             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11397         }
11398         
11399         if(this.indicator){
11400             this.indicator.removeClass('visible');
11401             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11402         }
11403         
11404         if(this.disabled){
11405             return;
11406         }
11407         
11408            
11409         if(this.allowBlank && !this.getRawValue().length){
11410             return;
11411         }
11412         if (Roo.bootstrap.version == 3) {
11413             this.el.addClass(this.validClass);
11414         } else {
11415             this.inputEl().addClass('is-valid');
11416         }
11417
11418         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11419             
11420             var feedback = this.el.select('.form-control-feedback', true).first();
11421             
11422             if(feedback){
11423                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11424                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11425             }
11426             
11427         }
11428         
11429         this.fireEvent('valid', this);
11430     },
11431     
11432      /**
11433      * Mark this field as invalid
11434      * @param {String} msg The validation message
11435      */
11436     markInvalid : function(msg)
11437     {
11438         if(!this.el  || this.preventMark){ // not rendered
11439             return;
11440         }
11441         
11442         this.el.removeClass([this.invalidClass, this.validClass]);
11443         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11444         
11445         var feedback = this.el.select('.form-control-feedback', true).first();
11446             
11447         if(feedback){
11448             this.el.select('.form-control-feedback', true).first().removeClass(
11449                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11450         }
11451
11452         if(this.disabled){
11453             return;
11454         }
11455         
11456         if(this.allowBlank && !this.getRawValue().length){
11457             return;
11458         }
11459         
11460         if(this.indicator){
11461             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11462             this.indicator.addClass('visible');
11463         }
11464         if (Roo.bootstrap.version == 3) {
11465             this.el.addClass(this.invalidClass);
11466         } else {
11467             this.inputEl().addClass('is-invalid');
11468         }
11469         
11470         
11471         
11472         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11473             
11474             var feedback = this.el.select('.form-control-feedback', true).first();
11475             
11476             if(feedback){
11477                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11478                 
11479                 if(this.getValue().length || this.forceFeedback){
11480                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11481                 }
11482                 
11483             }
11484             
11485         }
11486         
11487         this.fireEvent('invalid', this, msg);
11488     },
11489     // private
11490     SafariOnKeyDown : function(event)
11491     {
11492         // this is a workaround for a password hang bug on chrome/ webkit.
11493         if (this.inputEl().dom.type != 'password') {
11494             return;
11495         }
11496         
11497         var isSelectAll = false;
11498         
11499         if(this.inputEl().dom.selectionEnd > 0){
11500             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11501         }
11502         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11503             event.preventDefault();
11504             this.setValue('');
11505             return;
11506         }
11507         
11508         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11509             
11510             event.preventDefault();
11511             // this is very hacky as keydown always get's upper case.
11512             //
11513             var cc = String.fromCharCode(event.getCharCode());
11514             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11515             
11516         }
11517     },
11518     adjustWidth : function(tag, w){
11519         tag = tag.toLowerCase();
11520         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11521             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11522                 if(tag == 'input'){
11523                     return w + 2;
11524                 }
11525                 if(tag == 'textarea'){
11526                     return w-2;
11527                 }
11528             }else if(Roo.isOpera){
11529                 if(tag == 'input'){
11530                     return w + 2;
11531                 }
11532                 if(tag == 'textarea'){
11533                     return w-2;
11534                 }
11535             }
11536         }
11537         return w;
11538     },
11539     
11540     setFieldLabel : function(v)
11541     {
11542         if(!this.rendered){
11543             return;
11544         }
11545         
11546         if(this.indicatorEl()){
11547             var ar = this.el.select('label > span',true);
11548             
11549             if (ar.elements.length) {
11550                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11551                 this.fieldLabel = v;
11552                 return;
11553             }
11554             
11555             var br = this.el.select('label',true);
11556             
11557             if(br.elements.length) {
11558                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11559                 this.fieldLabel = v;
11560                 return;
11561             }
11562             
11563             Roo.log('Cannot Found any of label > span || label in input');
11564             return;
11565         }
11566         
11567         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11568         this.fieldLabel = v;
11569         
11570         
11571     }
11572 });
11573
11574  
11575 /*
11576  * - LGPL
11577  *
11578  * Input
11579  * 
11580  */
11581
11582 /**
11583  * @class Roo.bootstrap.TextArea
11584  * @extends Roo.bootstrap.Input
11585  * Bootstrap TextArea class
11586  * @cfg {Number} cols Specifies the visible width of a text area
11587  * @cfg {Number} rows Specifies the visible number of lines in a text area
11588  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11589  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11590  * @cfg {string} html text
11591  * 
11592  * @constructor
11593  * Create a new TextArea
11594  * @param {Object} config The config object
11595  */
11596
11597 Roo.bootstrap.TextArea = function(config){
11598     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11599    
11600 };
11601
11602 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11603      
11604     cols : false,
11605     rows : 5,
11606     readOnly : false,
11607     warp : 'soft',
11608     resize : false,
11609     value: false,
11610     html: false,
11611     
11612     getAutoCreate : function(){
11613         
11614         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11615         
11616         var id = Roo.id();
11617         
11618         var cfg = {};
11619         
11620         if(this.inputType != 'hidden'){
11621             cfg.cls = 'form-group' //input-group
11622         }
11623         
11624         var input =  {
11625             tag: 'textarea',
11626             id : id,
11627             warp : this.warp,
11628             rows : this.rows,
11629             value : this.value || '',
11630             html: this.html || '',
11631             cls : 'form-control',
11632             placeholder : this.placeholder || '' 
11633             
11634         };
11635         
11636         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11637             input.maxLength = this.maxLength;
11638         }
11639         
11640         if(this.resize){
11641             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11642         }
11643         
11644         if(this.cols){
11645             input.cols = this.cols;
11646         }
11647         
11648         if (this.readOnly) {
11649             input.readonly = true;
11650         }
11651         
11652         if (this.name) {
11653             input.name = this.name;
11654         }
11655         
11656         if (this.size) {
11657             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11658         }
11659         
11660         var settings=this;
11661         ['xs','sm','md','lg'].map(function(size){
11662             if (settings[size]) {
11663                 cfg.cls += ' col-' + size + '-' + settings[size];
11664             }
11665         });
11666         
11667         var inputblock = input;
11668         
11669         if(this.hasFeedback && !this.allowBlank){
11670             
11671             var feedback = {
11672                 tag: 'span',
11673                 cls: 'glyphicon form-control-feedback'
11674             };
11675
11676             inputblock = {
11677                 cls : 'has-feedback',
11678                 cn :  [
11679                     input,
11680                     feedback
11681                 ] 
11682             };  
11683         }
11684         
11685         
11686         if (this.before || this.after) {
11687             
11688             inputblock = {
11689                 cls : 'input-group',
11690                 cn :  [] 
11691             };
11692             if (this.before) {
11693                 inputblock.cn.push({
11694                     tag :'span',
11695                     cls : 'input-group-addon',
11696                     html : this.before
11697                 });
11698             }
11699             
11700             inputblock.cn.push(input);
11701             
11702             if(this.hasFeedback && !this.allowBlank){
11703                 inputblock.cls += ' has-feedback';
11704                 inputblock.cn.push(feedback);
11705             }
11706             
11707             if (this.after) {
11708                 inputblock.cn.push({
11709                     tag :'span',
11710                     cls : 'input-group-addon',
11711                     html : this.after
11712                 });
11713             }
11714             
11715         }
11716         
11717         if (align ==='left' && this.fieldLabel.length) {
11718             cfg.cn = [
11719                 {
11720                     tag: 'label',
11721                     'for' :  id,
11722                     cls : 'control-label',
11723                     html : this.fieldLabel
11724                 },
11725                 {
11726                     cls : "",
11727                     cn: [
11728                         inputblock
11729                     ]
11730                 }
11731
11732             ];
11733             
11734             if(this.labelWidth > 12){
11735                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11736             }
11737
11738             if(this.labelWidth < 13 && this.labelmd == 0){
11739                 this.labelmd = this.labelWidth;
11740             }
11741
11742             if(this.labellg > 0){
11743                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11744                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11745             }
11746
11747             if(this.labelmd > 0){
11748                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11749                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11750             }
11751
11752             if(this.labelsm > 0){
11753                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11754                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11755             }
11756
11757             if(this.labelxs > 0){
11758                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11759                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11760             }
11761             
11762         } else if ( this.fieldLabel.length) {
11763             cfg.cn = [
11764
11765                {
11766                    tag: 'label',
11767                    //cls : 'input-group-addon',
11768                    html : this.fieldLabel
11769
11770                },
11771
11772                inputblock
11773
11774            ];
11775
11776         } else {
11777
11778             cfg.cn = [
11779
11780                 inputblock
11781
11782             ];
11783                 
11784         }
11785         
11786         if (this.disabled) {
11787             input.disabled=true;
11788         }
11789         
11790         return cfg;
11791         
11792     },
11793     /**
11794      * return the real textarea element.
11795      */
11796     inputEl: function ()
11797     {
11798         return this.el.select('textarea.form-control',true).first();
11799     },
11800     
11801     /**
11802      * Clear any invalid styles/messages for this field
11803      */
11804     clearInvalid : function()
11805     {
11806         
11807         if(!this.el || this.preventMark){ // not rendered
11808             return;
11809         }
11810         
11811         var label = this.el.select('label', true).first();
11812         var icon = this.el.select('i.fa-star', true).first();
11813         
11814         if(label && icon){
11815             icon.remove();
11816         }
11817         this.el.removeClass( this.validClass);
11818         this.inputEl().removeClass('is-invalid');
11819          
11820         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11821             
11822             var feedback = this.el.select('.form-control-feedback', true).first();
11823             
11824             if(feedback){
11825                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11826             }
11827             
11828         }
11829         
11830         this.fireEvent('valid', this);
11831     },
11832     
11833      /**
11834      * Mark this field as valid
11835      */
11836     markValid : function()
11837     {
11838         if(!this.el  || this.preventMark){ // not rendered
11839             return;
11840         }
11841         
11842         this.el.removeClass([this.invalidClass, this.validClass]);
11843         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11844         
11845         var feedback = this.el.select('.form-control-feedback', true).first();
11846             
11847         if(feedback){
11848             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11849         }
11850
11851         if(this.disabled || this.allowBlank){
11852             return;
11853         }
11854         
11855         var label = this.el.select('label', true).first();
11856         var icon = this.el.select('i.fa-star', true).first();
11857         
11858         if(label && icon){
11859             icon.remove();
11860         }
11861         if (Roo.bootstrap.version == 3) {
11862             this.el.addClass(this.validClass);
11863         } else {
11864             this.inputEl().addClass('is-valid');
11865         }
11866         
11867         
11868         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11869             
11870             var feedback = this.el.select('.form-control-feedback', true).first();
11871             
11872             if(feedback){
11873                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11874                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11875             }
11876             
11877         }
11878         
11879         this.fireEvent('valid', this);
11880     },
11881     
11882      /**
11883      * Mark this field as invalid
11884      * @param {String} msg The validation message
11885      */
11886     markInvalid : function(msg)
11887     {
11888         if(!this.el  || this.preventMark){ // not rendered
11889             return;
11890         }
11891         
11892         this.el.removeClass([this.invalidClass, this.validClass]);
11893         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11894         
11895         var feedback = this.el.select('.form-control-feedback', true).first();
11896             
11897         if(feedback){
11898             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11899         }
11900
11901         if(this.disabled || this.allowBlank){
11902             return;
11903         }
11904         
11905         var label = this.el.select('label', true).first();
11906         var icon = this.el.select('i.fa-star', true).first();
11907         
11908         if(!this.getValue().length && label && !icon){
11909             this.el.createChild({
11910                 tag : 'i',
11911                 cls : 'text-danger fa fa-lg fa-star',
11912                 tooltip : 'This field is required',
11913                 style : 'margin-right:5px;'
11914             }, label, true);
11915         }
11916         
11917         if (Roo.bootstrap.version == 3) {
11918             this.el.addClass(this.invalidClass);
11919         } else {
11920             this.inputEl().addClass('is-invalid');
11921         }
11922         
11923         // fixme ... this may be depricated need to test..
11924         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11925             
11926             var feedback = this.el.select('.form-control-feedback', true).first();
11927             
11928             if(feedback){
11929                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11930                 
11931                 if(this.getValue().length || this.forceFeedback){
11932                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11933                 }
11934                 
11935             }
11936             
11937         }
11938         
11939         this.fireEvent('invalid', this, msg);
11940     }
11941 });
11942
11943  
11944 /*
11945  * - LGPL
11946  *
11947  * trigger field - base class for combo..
11948  * 
11949  */
11950  
11951 /**
11952  * @class Roo.bootstrap.TriggerField
11953  * @extends Roo.bootstrap.Input
11954  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11955  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11956  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11957  * for which you can provide a custom implementation.  For example:
11958  * <pre><code>
11959 var trigger = new Roo.bootstrap.TriggerField();
11960 trigger.onTriggerClick = myTriggerFn;
11961 trigger.applyTo('my-field');
11962 </code></pre>
11963  *
11964  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11965  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11966  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11967  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11968  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11969
11970  * @constructor
11971  * Create a new TriggerField.
11972  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11973  * to the base TextField)
11974  */
11975 Roo.bootstrap.TriggerField = function(config){
11976     this.mimicing = false;
11977     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11978 };
11979
11980 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11981     /**
11982      * @cfg {String} triggerClass A CSS class to apply to the trigger
11983      */
11984      /**
11985      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11986      */
11987     hideTrigger:false,
11988
11989     /**
11990      * @cfg {Boolean} removable (true|false) special filter default false
11991      */
11992     removable : false,
11993     
11994     /** @cfg {Boolean} grow @hide */
11995     /** @cfg {Number} growMin @hide */
11996     /** @cfg {Number} growMax @hide */
11997
11998     /**
11999      * @hide 
12000      * @method
12001      */
12002     autoSize: Roo.emptyFn,
12003     // private
12004     monitorTab : true,
12005     // private
12006     deferHeight : true,
12007
12008     
12009     actionMode : 'wrap',
12010     
12011     caret : false,
12012     
12013     
12014     getAutoCreate : function(){
12015        
12016         var align = this.labelAlign || this.parentLabelAlign();
12017         
12018         var id = Roo.id();
12019         
12020         var cfg = {
12021             cls: 'form-group' //input-group
12022         };
12023         
12024         
12025         var input =  {
12026             tag: 'input',
12027             id : id,
12028             type : this.inputType,
12029             cls : 'form-control',
12030             autocomplete: 'new-password',
12031             placeholder : this.placeholder || '' 
12032             
12033         };
12034         if (this.name) {
12035             input.name = this.name;
12036         }
12037         if (this.size) {
12038             input.cls += ' input-' + this.size;
12039         }
12040         
12041         if (this.disabled) {
12042             input.disabled=true;
12043         }
12044         
12045         var inputblock = input;
12046         
12047         if(this.hasFeedback && !this.allowBlank){
12048             
12049             var feedback = {
12050                 tag: 'span',
12051                 cls: 'glyphicon form-control-feedback'
12052             };
12053             
12054             if(this.removable && !this.editable  ){
12055                 inputblock = {
12056                     cls : 'has-feedback',
12057                     cn :  [
12058                         inputblock,
12059                         {
12060                             tag: 'button',
12061                             html : 'x',
12062                             cls : 'roo-combo-removable-btn close'
12063                         },
12064                         feedback
12065                     ] 
12066                 };
12067             } else {
12068                 inputblock = {
12069                     cls : 'has-feedback',
12070                     cn :  [
12071                         inputblock,
12072                         feedback
12073                     ] 
12074                 };
12075             }
12076
12077         } else {
12078             if(this.removable && !this.editable ){
12079                 inputblock = {
12080                     cls : 'roo-removable',
12081                     cn :  [
12082                         inputblock,
12083                         {
12084                             tag: 'button',
12085                             html : 'x',
12086                             cls : 'roo-combo-removable-btn close'
12087                         }
12088                     ] 
12089                 };
12090             }
12091         }
12092         
12093         if (this.before || this.after) {
12094             
12095             inputblock = {
12096                 cls : 'input-group',
12097                 cn :  [] 
12098             };
12099             if (this.before) {
12100                 inputblock.cn.push({
12101                     tag :'span',
12102                     cls : 'input-group-addon input-group-prepend input-group-text',
12103                     html : this.before
12104                 });
12105             }
12106             
12107             inputblock.cn.push(input);
12108             
12109             if(this.hasFeedback && !this.allowBlank){
12110                 inputblock.cls += ' has-feedback';
12111                 inputblock.cn.push(feedback);
12112             }
12113             
12114             if (this.after) {
12115                 inputblock.cn.push({
12116                     tag :'span',
12117                     cls : 'input-group-addon input-group-append input-group-text',
12118                     html : this.after
12119                 });
12120             }
12121             
12122         };
12123         
12124       
12125         
12126         var ibwrap = inputblock;
12127         
12128         if(this.multiple){
12129             ibwrap = {
12130                 tag: 'ul',
12131                 cls: 'roo-select2-choices',
12132                 cn:[
12133                     {
12134                         tag: 'li',
12135                         cls: 'roo-select2-search-field',
12136                         cn: [
12137
12138                             inputblock
12139                         ]
12140                     }
12141                 ]
12142             };
12143                 
12144         }
12145         
12146         var combobox = {
12147             cls: 'roo-select2-container input-group',
12148             cn: [
12149                  {
12150                     tag: 'input',
12151                     type : 'hidden',
12152                     cls: 'form-hidden-field'
12153                 },
12154                 ibwrap
12155             ]
12156         };
12157         
12158         if(!this.multiple && this.showToggleBtn){
12159             
12160             var caret = {
12161                         tag: 'span',
12162                         cls: 'caret'
12163              };
12164             if (this.caret != false) {
12165                 caret = {
12166                      tag: 'i',
12167                      cls: 'fa fa-' + this.caret
12168                 };
12169                 
12170             }
12171             
12172             combobox.cn.push({
12173                 tag :'span',
12174                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12175                 cn : [
12176                     Roo.bootstrap.version == 3 ? caret : '',
12177                     {
12178                         tag: 'span',
12179                         cls: 'combobox-clear',
12180                         cn  : [
12181                             {
12182                                 tag : 'i',
12183                                 cls: 'icon-remove'
12184                             }
12185                         ]
12186                     }
12187                 ]
12188
12189             })
12190         }
12191         
12192         if(this.multiple){
12193             combobox.cls += ' roo-select2-container-multi';
12194         }
12195          var indicator = {
12196             tag : 'i',
12197             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12198             tooltip : 'This field is required'
12199         };
12200         if (Roo.bootstrap.version == 4) {
12201             indicator = {
12202                 tag : 'i',
12203                 style : 'display:none'
12204             };
12205         }
12206         
12207         
12208         if (align ==='left' && this.fieldLabel.length) {
12209             
12210             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12211
12212             cfg.cn = [
12213                 indicator,
12214                 {
12215                     tag: 'label',
12216                     'for' :  id,
12217                     cls : 'control-label',
12218                     html : this.fieldLabel
12219
12220                 },
12221                 {
12222                     cls : "", 
12223                     cn: [
12224                         combobox
12225                     ]
12226                 }
12227
12228             ];
12229             
12230             var labelCfg = cfg.cn[1];
12231             var contentCfg = cfg.cn[2];
12232             
12233             if(this.indicatorpos == 'right'){
12234                 cfg.cn = [
12235                     {
12236                         tag: 'label',
12237                         'for' :  id,
12238                         cls : 'control-label',
12239                         cn : [
12240                             {
12241                                 tag : 'span',
12242                                 html : this.fieldLabel
12243                             },
12244                             indicator
12245                         ]
12246                     },
12247                     {
12248                         cls : "", 
12249                         cn: [
12250                             combobox
12251                         ]
12252                     }
12253
12254                 ];
12255                 
12256                 labelCfg = cfg.cn[0];
12257                 contentCfg = cfg.cn[1];
12258             }
12259             
12260             if(this.labelWidth > 12){
12261                 labelCfg.style = "width: " + this.labelWidth + 'px';
12262             }
12263             
12264             if(this.labelWidth < 13 && this.labelmd == 0){
12265                 this.labelmd = this.labelWidth;
12266             }
12267             
12268             if(this.labellg > 0){
12269                 labelCfg.cls += ' col-lg-' + this.labellg;
12270                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12271             }
12272             
12273             if(this.labelmd > 0){
12274                 labelCfg.cls += ' col-md-' + this.labelmd;
12275                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12276             }
12277             
12278             if(this.labelsm > 0){
12279                 labelCfg.cls += ' col-sm-' + this.labelsm;
12280                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12281             }
12282             
12283             if(this.labelxs > 0){
12284                 labelCfg.cls += ' col-xs-' + this.labelxs;
12285                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12286             }
12287             
12288         } else if ( this.fieldLabel.length) {
12289 //                Roo.log(" label");
12290             cfg.cn = [
12291                 indicator,
12292                {
12293                    tag: 'label',
12294                    //cls : 'input-group-addon',
12295                    html : this.fieldLabel
12296
12297                },
12298
12299                combobox
12300
12301             ];
12302             
12303             if(this.indicatorpos == 'right'){
12304                 
12305                 cfg.cn = [
12306                     {
12307                        tag: 'label',
12308                        cn : [
12309                            {
12310                                tag : 'span',
12311                                html : this.fieldLabel
12312                            },
12313                            indicator
12314                        ]
12315
12316                     },
12317                     combobox
12318
12319                 ];
12320
12321             }
12322
12323         } else {
12324             
12325 //                Roo.log(" no label && no align");
12326                 cfg = combobox
12327                      
12328                 
12329         }
12330         
12331         var settings=this;
12332         ['xs','sm','md','lg'].map(function(size){
12333             if (settings[size]) {
12334                 cfg.cls += ' col-' + size + '-' + settings[size];
12335             }
12336         });
12337         
12338         return cfg;
12339         
12340     },
12341     
12342     
12343     
12344     // private
12345     onResize : function(w, h){
12346 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12347 //        if(typeof w == 'number'){
12348 //            var x = w - this.trigger.getWidth();
12349 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12350 //            this.trigger.setStyle('left', x+'px');
12351 //        }
12352     },
12353
12354     // private
12355     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12356
12357     // private
12358     getResizeEl : function(){
12359         return this.inputEl();
12360     },
12361
12362     // private
12363     getPositionEl : function(){
12364         return this.inputEl();
12365     },
12366
12367     // private
12368     alignErrorIcon : function(){
12369         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12370     },
12371
12372     // private
12373     initEvents : function(){
12374         
12375         this.createList();
12376         
12377         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12378         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12379         if(!this.multiple && this.showToggleBtn){
12380             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12381             if(this.hideTrigger){
12382                 this.trigger.setDisplayed(false);
12383             }
12384             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12385         }
12386         
12387         if(this.multiple){
12388             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12389         }
12390         
12391         if(this.removable && !this.editable && !this.tickable){
12392             var close = this.closeTriggerEl();
12393             
12394             if(close){
12395                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12396                 close.on('click', this.removeBtnClick, this, close);
12397             }
12398         }
12399         
12400         //this.trigger.addClassOnOver('x-form-trigger-over');
12401         //this.trigger.addClassOnClick('x-form-trigger-click');
12402         
12403         //if(!this.width){
12404         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12405         //}
12406     },
12407     
12408     closeTriggerEl : function()
12409     {
12410         var close = this.el.select('.roo-combo-removable-btn', true).first();
12411         return close ? close : false;
12412     },
12413     
12414     removeBtnClick : function(e, h, el)
12415     {
12416         e.preventDefault();
12417         
12418         if(this.fireEvent("remove", this) !== false){
12419             this.reset();
12420             this.fireEvent("afterremove", this)
12421         }
12422     },
12423     
12424     createList : function()
12425     {
12426         this.list = Roo.get(document.body).createChild({
12427             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12428             cls: 'typeahead typeahead-long dropdown-menu shadow',
12429             style: 'display:none'
12430         });
12431         
12432         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12433         
12434     },
12435
12436     // private
12437     initTrigger : function(){
12438        
12439     },
12440
12441     // private
12442     onDestroy : function(){
12443         if(this.trigger){
12444             this.trigger.removeAllListeners();
12445           //  this.trigger.remove();
12446         }
12447         //if(this.wrap){
12448         //    this.wrap.remove();
12449         //}
12450         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12451     },
12452
12453     // private
12454     onFocus : function(){
12455         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12456         /*
12457         if(!this.mimicing){
12458             this.wrap.addClass('x-trigger-wrap-focus');
12459             this.mimicing = true;
12460             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12461             if(this.monitorTab){
12462                 this.el.on("keydown", this.checkTab, this);
12463             }
12464         }
12465         */
12466     },
12467
12468     // private
12469     checkTab : function(e){
12470         if(e.getKey() == e.TAB){
12471             this.triggerBlur();
12472         }
12473     },
12474
12475     // private
12476     onBlur : function(){
12477         // do nothing
12478     },
12479
12480     // private
12481     mimicBlur : function(e, t){
12482         /*
12483         if(!this.wrap.contains(t) && this.validateBlur()){
12484             this.triggerBlur();
12485         }
12486         */
12487     },
12488
12489     // private
12490     triggerBlur : function(){
12491         this.mimicing = false;
12492         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12493         if(this.monitorTab){
12494             this.el.un("keydown", this.checkTab, this);
12495         }
12496         //this.wrap.removeClass('x-trigger-wrap-focus');
12497         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12498     },
12499
12500     // private
12501     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12502     validateBlur : function(e, t){
12503         return true;
12504     },
12505
12506     // private
12507     onDisable : function(){
12508         this.inputEl().dom.disabled = true;
12509         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12510         //if(this.wrap){
12511         //    this.wrap.addClass('x-item-disabled');
12512         //}
12513     },
12514
12515     // private
12516     onEnable : function(){
12517         this.inputEl().dom.disabled = false;
12518         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12519         //if(this.wrap){
12520         //    this.el.removeClass('x-item-disabled');
12521         //}
12522     },
12523
12524     // private
12525     onShow : function(){
12526         var ae = this.getActionEl();
12527         
12528         if(ae){
12529             ae.dom.style.display = '';
12530             ae.dom.style.visibility = 'visible';
12531         }
12532     },
12533
12534     // private
12535     
12536     onHide : function(){
12537         var ae = this.getActionEl();
12538         ae.dom.style.display = 'none';
12539     },
12540
12541     /**
12542      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12543      * by an implementing function.
12544      * @method
12545      * @param {EventObject} e
12546      */
12547     onTriggerClick : Roo.emptyFn
12548 });
12549  
12550 /*
12551 * Licence: LGPL
12552 */
12553
12554 /**
12555  * @class Roo.bootstrap.CardUploader
12556  * @extends Roo.bootstrap.Button
12557  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12558  * @cfg {Number} errorTimeout default 3000
12559  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12560  * @cfg {Array}  html The button text.
12561
12562  *
12563  * @constructor
12564  * Create a new CardUploader
12565  * @param {Object} config The config object
12566  */
12567
12568 Roo.bootstrap.CardUploader = function(config){
12569     
12570  
12571     
12572     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12573     
12574     
12575     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12576         return r.data.id
12577      });
12578     
12579      this.addEvents({
12580          // raw events
12581         /**
12582          * @event preview
12583          * When a image is clicked on - and needs to display a slideshow or similar..
12584          * @param {Roo.bootstrap.Card} this
12585          * @param {Object} The image information data 
12586          *
12587          */
12588         'preview' : true,
12589          /**
12590          * @event download
12591          * When a the download link is clicked
12592          * @param {Roo.bootstrap.Card} this
12593          * @param {Object} The image information data  contains 
12594          */
12595         'download' : true
12596         
12597     });
12598 };
12599  
12600 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12601     
12602      
12603     errorTimeout : 3000,
12604      
12605     images : false,
12606    
12607     fileCollection : false,
12608     allowBlank : true,
12609     
12610     getAutoCreate : function()
12611     {
12612         
12613         var cfg =  {
12614             cls :'form-group' ,
12615             cn : [
12616                
12617                 {
12618                     tag: 'label',
12619                    //cls : 'input-group-addon',
12620                     html : this.fieldLabel
12621
12622                 },
12623
12624                 {
12625                     tag: 'input',
12626                     type : 'hidden',
12627                     name : this.name,
12628                     value : this.value,
12629                     cls : 'd-none  form-control'
12630                 },
12631                 
12632                 {
12633                     tag: 'input',
12634                     multiple : 'multiple',
12635                     type : 'file',
12636                     cls : 'd-none  roo-card-upload-selector'
12637                 },
12638                 
12639                 {
12640                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12641                 },
12642                 {
12643                     cls : 'card-columns roo-card-uploader-container'
12644                 }
12645
12646             ]
12647         };
12648            
12649          
12650         return cfg;
12651     },
12652     
12653     getChildContainer : function() /// what children are added to.
12654     {
12655         return this.containerEl;
12656     },
12657    
12658     getButtonContainer : function() /// what children are added to.
12659     {
12660         return this.el.select(".roo-card-uploader-button-container").first();
12661     },
12662    
12663     initEvents : function()
12664     {
12665         
12666         Roo.bootstrap.Input.prototype.initEvents.call(this);
12667         
12668         var t = this;
12669         this.addxtype({
12670             xns: Roo.bootstrap,
12671
12672             xtype : 'Button',
12673             container_method : 'getButtonContainer' ,            
12674             html :  this.html, // fix changable?
12675             cls : 'w-100 ',
12676             listeners : {
12677                 'click' : function(btn, e) {
12678                     t.onClick(e);
12679                 }
12680             }
12681         });
12682         
12683         
12684         
12685         
12686         this.urlAPI = (window.createObjectURL && window) || 
12687                                 (window.URL && URL.revokeObjectURL && URL) || 
12688                                 (window.webkitURL && webkitURL);
12689                         
12690          
12691          
12692          
12693         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12694         
12695         this.selectorEl.on('change', this.onFileSelected, this);
12696         if (this.images) {
12697             var t = this;
12698             this.images.forEach(function(img) {
12699                 t.addCard(img)
12700             });
12701             this.images = false;
12702         }
12703         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12704          
12705        
12706     },
12707     
12708    
12709     onClick : function(e)
12710     {
12711         e.preventDefault();
12712          
12713         this.selectorEl.dom.click();
12714          
12715     },
12716     
12717     onFileSelected : function(e)
12718     {
12719         e.preventDefault();
12720         
12721         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12722             return;
12723         }
12724         
12725         Roo.each(this.selectorEl.dom.files, function(file){    
12726             this.addFile(file);
12727         }, this);
12728          
12729     },
12730     
12731       
12732     
12733       
12734     
12735     addFile : function(file)
12736     {
12737            
12738         if(typeof(file) === 'string'){
12739             throw "Add file by name?"; // should not happen
12740             return;
12741         }
12742         
12743         if(!file || !this.urlAPI){
12744             return;
12745         }
12746         
12747         // file;
12748         // file.type;
12749         
12750         var _this = this;
12751         
12752         
12753         var url = _this.urlAPI.createObjectURL( file);
12754            
12755         this.addCard({
12756             id : Roo.bootstrap.CardUploader.ID--,
12757             is_uploaded : false,
12758             src : url,
12759             srcfile : file,
12760             title : file.name,
12761             mimetype : file.type,
12762             preview : false,
12763             is_deleted : 0
12764         });
12765         
12766     },
12767     
12768     /**
12769      * addCard - add an Attachment to the uploader
12770      * @param data - the data about the image to upload
12771      *
12772      * {
12773           id : 123
12774           title : "Title of file",
12775           is_uploaded : false,
12776           src : "http://.....",
12777           srcfile : { the File upload object },
12778           mimetype : file.type,
12779           preview : false,
12780           is_deleted : 0
12781           .. any other data...
12782         }
12783      *
12784      * 
12785     */
12786     
12787     addCard : function (data)
12788     {
12789         // hidden input element?
12790         // if the file is not an image...
12791         //then we need to use something other that and header_image
12792         var t = this;
12793         //   remove.....
12794         var footer = [
12795             {
12796                 xns : Roo.bootstrap,
12797                 xtype : 'CardFooter',
12798                  items: [
12799                     {
12800                         xns : Roo.bootstrap,
12801                         xtype : 'Element',
12802                         cls : 'd-flex',
12803                         items : [
12804                             
12805                             {
12806                                 xns : Roo.bootstrap,
12807                                 xtype : 'Button',
12808                                 html : String.format("<small>{0}</small>", data.title),
12809                                 cls : 'col-10 text-left',
12810                                 size: 'sm',
12811                                 weight: 'link',
12812                                 fa : 'download',
12813                                 listeners : {
12814                                     click : function() {
12815                                      
12816                                         t.fireEvent( "download", t, data );
12817                                     }
12818                                 }
12819                             },
12820                           
12821                             {
12822                                 xns : Roo.bootstrap,
12823                                 xtype : 'Button',
12824                                 style: 'max-height: 28px; ',
12825                                 size : 'sm',
12826                                 weight: 'danger',
12827                                 cls : 'col-2',
12828                                 fa : 'times',
12829                                 listeners : {
12830                                     click : function() {
12831                                         t.removeCard(data.id)
12832                                     }
12833                                 }
12834                             }
12835                         ]
12836                     }
12837                     
12838                 ] 
12839             }
12840             
12841         ];
12842         
12843         var cn = this.addxtype(
12844             {
12845                  
12846                 xns : Roo.bootstrap,
12847                 xtype : 'Card',
12848                 closeable : true,
12849                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12850                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12851                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12852                 data : data,
12853                 html : false,
12854                  
12855                 items : footer,
12856                 initEvents : function() {
12857                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12858                     var card = this;
12859                     this.imgEl = this.el.select('.card-img-top').first();
12860                     if (this.imgEl) {
12861                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
12862                         this.imgEl.set({ 'pointer' : 'cursor' });
12863                                   
12864                     }
12865                     this.getCardFooter().addClass('p-1');
12866                     
12867                   
12868                 }
12869                 
12870             }
12871         );
12872         // dont' really need ot update items.
12873         // this.items.push(cn);
12874         this.fileCollection.add(cn);
12875         
12876         if (!data.srcfile) {
12877             this.updateInput();
12878             return;
12879         }
12880             
12881         var _t = this;
12882         var reader = new FileReader();
12883         reader.addEventListener("load", function() {  
12884             data.srcdata =  reader.result;
12885             _t.updateInput();
12886         });
12887         reader.readAsDataURL(data.srcfile);
12888         
12889         
12890         
12891     },
12892     removeCard : function(id)
12893     {
12894         
12895         var card  = this.fileCollection.get(id);
12896         card.data.is_deleted = 1;
12897         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12898         //this.fileCollection.remove(card);
12899         //this.items = this.items.filter(function(e) { return e != card });
12900         // dont' really need ot update items.
12901         card.el.dom.parentNode.removeChild(card.el.dom);
12902         this.updateInput();
12903
12904         
12905     },
12906     reset: function()
12907     {
12908         this.fileCollection.each(function(card) {
12909             if (card.el.dom && card.el.dom.parentNode) {
12910                 card.el.dom.parentNode.removeChild(card.el.dom);
12911             }
12912         });
12913         this.fileCollection.clear();
12914         this.updateInput();
12915     },
12916     
12917     updateInput : function()
12918     {
12919          var data = [];
12920         this.fileCollection.each(function(e) {
12921             data.push(e.data);
12922             
12923         });
12924         this.inputEl().dom.value = JSON.stringify(data);
12925         
12926         
12927         
12928     }
12929     
12930     
12931 });
12932
12933
12934 Roo.bootstrap.CardUploader.ID = -1;/*
12935  * Based on:
12936  * Ext JS Library 1.1.1
12937  * Copyright(c) 2006-2007, Ext JS, LLC.
12938  *
12939  * Originally Released Under LGPL - original licence link has changed is not relivant.
12940  *
12941  * Fork - LGPL
12942  * <script type="text/javascript">
12943  */
12944
12945
12946 /**
12947  * @class Roo.data.SortTypes
12948  * @singleton
12949  * Defines the default sorting (casting?) comparison functions used when sorting data.
12950  */
12951 Roo.data.SortTypes = {
12952     /**
12953      * Default sort that does nothing
12954      * @param {Mixed} s The value being converted
12955      * @return {Mixed} The comparison value
12956      */
12957     none : function(s){
12958         return s;
12959     },
12960     
12961     /**
12962      * The regular expression used to strip tags
12963      * @type {RegExp}
12964      * @property
12965      */
12966     stripTagsRE : /<\/?[^>]+>/gi,
12967     
12968     /**
12969      * Strips all HTML tags to sort on text only
12970      * @param {Mixed} s The value being converted
12971      * @return {String} The comparison value
12972      */
12973     asText : function(s){
12974         return String(s).replace(this.stripTagsRE, "");
12975     },
12976     
12977     /**
12978      * Strips all HTML tags to sort on text only - Case insensitive
12979      * @param {Mixed} s The value being converted
12980      * @return {String} The comparison value
12981      */
12982     asUCText : function(s){
12983         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12984     },
12985     
12986     /**
12987      * Case insensitive string
12988      * @param {Mixed} s The value being converted
12989      * @return {String} The comparison value
12990      */
12991     asUCString : function(s) {
12992         return String(s).toUpperCase();
12993     },
12994     
12995     /**
12996      * Date sorting
12997      * @param {Mixed} s The value being converted
12998      * @return {Number} The comparison value
12999      */
13000     asDate : function(s) {
13001         if(!s){
13002             return 0;
13003         }
13004         if(s instanceof Date){
13005             return s.getTime();
13006         }
13007         return Date.parse(String(s));
13008     },
13009     
13010     /**
13011      * Float sorting
13012      * @param {Mixed} s The value being converted
13013      * @return {Float} The comparison value
13014      */
13015     asFloat : function(s) {
13016         var val = parseFloat(String(s).replace(/,/g, ""));
13017         if(isNaN(val)) {
13018             val = 0;
13019         }
13020         return val;
13021     },
13022     
13023     /**
13024      * Integer sorting
13025      * @param {Mixed} s The value being converted
13026      * @return {Number} The comparison value
13027      */
13028     asInt : function(s) {
13029         var val = parseInt(String(s).replace(/,/g, ""));
13030         if(isNaN(val)) {
13031             val = 0;
13032         }
13033         return val;
13034     }
13035 };/*
13036  * Based on:
13037  * Ext JS Library 1.1.1
13038  * Copyright(c) 2006-2007, Ext JS, LLC.
13039  *
13040  * Originally Released Under LGPL - original licence link has changed is not relivant.
13041  *
13042  * Fork - LGPL
13043  * <script type="text/javascript">
13044  */
13045
13046 /**
13047 * @class Roo.data.Record
13048  * Instances of this class encapsulate both record <em>definition</em> information, and record
13049  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13050  * to access Records cached in an {@link Roo.data.Store} object.<br>
13051  * <p>
13052  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13053  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13054  * objects.<br>
13055  * <p>
13056  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13057  * @constructor
13058  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13059  * {@link #create}. The parameters are the same.
13060  * @param {Array} data An associative Array of data values keyed by the field name.
13061  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13062  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13063  * not specified an integer id is generated.
13064  */
13065 Roo.data.Record = function(data, id){
13066     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13067     this.data = data;
13068 };
13069
13070 /**
13071  * Generate a constructor for a specific record layout.
13072  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13073  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13074  * Each field definition object may contain the following properties: <ul>
13075  * <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,
13076  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13077  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13078  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13079  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13080  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13081  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13082  * this may be omitted.</p></li>
13083  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13084  * <ul><li>auto (Default, implies no conversion)</li>
13085  * <li>string</li>
13086  * <li>int</li>
13087  * <li>float</li>
13088  * <li>boolean</li>
13089  * <li>date</li></ul></p></li>
13090  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13091  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13092  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13093  * by the Reader into an object that will be stored in the Record. It is passed the
13094  * following parameters:<ul>
13095  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13096  * </ul></p></li>
13097  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13098  * </ul>
13099  * <br>usage:<br><pre><code>
13100 var TopicRecord = Roo.data.Record.create(
13101     {name: 'title', mapping: 'topic_title'},
13102     {name: 'author', mapping: 'username'},
13103     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13104     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13105     {name: 'lastPoster', mapping: 'user2'},
13106     {name: 'excerpt', mapping: 'post_text'}
13107 );
13108
13109 var myNewRecord = new TopicRecord({
13110     title: 'Do my job please',
13111     author: 'noobie',
13112     totalPosts: 1,
13113     lastPost: new Date(),
13114     lastPoster: 'Animal',
13115     excerpt: 'No way dude!'
13116 });
13117 myStore.add(myNewRecord);
13118 </code></pre>
13119  * @method create
13120  * @static
13121  */
13122 Roo.data.Record.create = function(o){
13123     var f = function(){
13124         f.superclass.constructor.apply(this, arguments);
13125     };
13126     Roo.extend(f, Roo.data.Record);
13127     var p = f.prototype;
13128     p.fields = new Roo.util.MixedCollection(false, function(field){
13129         return field.name;
13130     });
13131     for(var i = 0, len = o.length; i < len; i++){
13132         p.fields.add(new Roo.data.Field(o[i]));
13133     }
13134     f.getField = function(name){
13135         return p.fields.get(name);  
13136     };
13137     return f;
13138 };
13139
13140 Roo.data.Record.AUTO_ID = 1000;
13141 Roo.data.Record.EDIT = 'edit';
13142 Roo.data.Record.REJECT = 'reject';
13143 Roo.data.Record.COMMIT = 'commit';
13144
13145 Roo.data.Record.prototype = {
13146     /**
13147      * Readonly flag - true if this record has been modified.
13148      * @type Boolean
13149      */
13150     dirty : false,
13151     editing : false,
13152     error: null,
13153     modified: null,
13154
13155     // private
13156     join : function(store){
13157         this.store = store;
13158     },
13159
13160     /**
13161      * Set the named field to the specified value.
13162      * @param {String} name The name of the field to set.
13163      * @param {Object} value The value to set the field to.
13164      */
13165     set : function(name, value){
13166         if(this.data[name] == value){
13167             return;
13168         }
13169         this.dirty = true;
13170         if(!this.modified){
13171             this.modified = {};
13172         }
13173         if(typeof this.modified[name] == 'undefined'){
13174             this.modified[name] = this.data[name];
13175         }
13176         this.data[name] = value;
13177         if(!this.editing && this.store){
13178             this.store.afterEdit(this);
13179         }       
13180     },
13181
13182     /**
13183      * Get the value of the named field.
13184      * @param {String} name The name of the field to get the value of.
13185      * @return {Object} The value of the field.
13186      */
13187     get : function(name){
13188         return this.data[name]; 
13189     },
13190
13191     // private
13192     beginEdit : function(){
13193         this.editing = true;
13194         this.modified = {}; 
13195     },
13196
13197     // private
13198     cancelEdit : function(){
13199         this.editing = false;
13200         delete this.modified;
13201     },
13202
13203     // private
13204     endEdit : function(){
13205         this.editing = false;
13206         if(this.dirty && this.store){
13207             this.store.afterEdit(this);
13208         }
13209     },
13210
13211     /**
13212      * Usually called by the {@link Roo.data.Store} which owns the Record.
13213      * Rejects all changes made to the Record since either creation, or the last commit operation.
13214      * Modified fields are reverted to their original values.
13215      * <p>
13216      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13217      * of reject operations.
13218      */
13219     reject : function(){
13220         var m = this.modified;
13221         for(var n in m){
13222             if(typeof m[n] != "function"){
13223                 this.data[n] = m[n];
13224             }
13225         }
13226         this.dirty = false;
13227         delete this.modified;
13228         this.editing = false;
13229         if(this.store){
13230             this.store.afterReject(this);
13231         }
13232     },
13233
13234     /**
13235      * Usually called by the {@link Roo.data.Store} which owns the Record.
13236      * Commits all changes made to the Record since either creation, or the last commit operation.
13237      * <p>
13238      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13239      * of commit operations.
13240      */
13241     commit : function(){
13242         this.dirty = false;
13243         delete this.modified;
13244         this.editing = false;
13245         if(this.store){
13246             this.store.afterCommit(this);
13247         }
13248     },
13249
13250     // private
13251     hasError : function(){
13252         return this.error != null;
13253     },
13254
13255     // private
13256     clearError : function(){
13257         this.error = null;
13258     },
13259
13260     /**
13261      * Creates a copy of this record.
13262      * @param {String} id (optional) A new record id if you don't want to use this record's id
13263      * @return {Record}
13264      */
13265     copy : function(newId) {
13266         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13267     }
13268 };/*
13269  * Based on:
13270  * Ext JS Library 1.1.1
13271  * Copyright(c) 2006-2007, Ext JS, LLC.
13272  *
13273  * Originally Released Under LGPL - original licence link has changed is not relivant.
13274  *
13275  * Fork - LGPL
13276  * <script type="text/javascript">
13277  */
13278
13279
13280
13281 /**
13282  * @class Roo.data.Store
13283  * @extends Roo.util.Observable
13284  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13285  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13286  * <p>
13287  * 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
13288  * has no knowledge of the format of the data returned by the Proxy.<br>
13289  * <p>
13290  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13291  * instances from the data object. These records are cached and made available through accessor functions.
13292  * @constructor
13293  * Creates a new Store.
13294  * @param {Object} config A config object containing the objects needed for the Store to access data,
13295  * and read the data into Records.
13296  */
13297 Roo.data.Store = function(config){
13298     this.data = new Roo.util.MixedCollection(false);
13299     this.data.getKey = function(o){
13300         return o.id;
13301     };
13302     this.baseParams = {};
13303     // private
13304     this.paramNames = {
13305         "start" : "start",
13306         "limit" : "limit",
13307         "sort" : "sort",
13308         "dir" : "dir",
13309         "multisort" : "_multisort"
13310     };
13311
13312     if(config && config.data){
13313         this.inlineData = config.data;
13314         delete config.data;
13315     }
13316
13317     Roo.apply(this, config);
13318     
13319     if(this.reader){ // reader passed
13320         this.reader = Roo.factory(this.reader, Roo.data);
13321         this.reader.xmodule = this.xmodule || false;
13322         if(!this.recordType){
13323             this.recordType = this.reader.recordType;
13324         }
13325         if(this.reader.onMetaChange){
13326             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13327         }
13328     }
13329
13330     if(this.recordType){
13331         this.fields = this.recordType.prototype.fields;
13332     }
13333     this.modified = [];
13334
13335     this.addEvents({
13336         /**
13337          * @event datachanged
13338          * Fires when the data cache has changed, and a widget which is using this Store
13339          * as a Record cache should refresh its view.
13340          * @param {Store} this
13341          */
13342         datachanged : true,
13343         /**
13344          * @event metachange
13345          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13346          * @param {Store} this
13347          * @param {Object} meta The JSON metadata
13348          */
13349         metachange : true,
13350         /**
13351          * @event add
13352          * Fires when Records have been added to the Store
13353          * @param {Store} this
13354          * @param {Roo.data.Record[]} records The array of Records added
13355          * @param {Number} index The index at which the record(s) were added
13356          */
13357         add : true,
13358         /**
13359          * @event remove
13360          * Fires when a Record has been removed from the Store
13361          * @param {Store} this
13362          * @param {Roo.data.Record} record The Record that was removed
13363          * @param {Number} index The index at which the record was removed
13364          */
13365         remove : true,
13366         /**
13367          * @event update
13368          * Fires when a Record has been updated
13369          * @param {Store} this
13370          * @param {Roo.data.Record} record The Record that was updated
13371          * @param {String} operation The update operation being performed.  Value may be one of:
13372          * <pre><code>
13373  Roo.data.Record.EDIT
13374  Roo.data.Record.REJECT
13375  Roo.data.Record.COMMIT
13376          * </code></pre>
13377          */
13378         update : true,
13379         /**
13380          * @event clear
13381          * Fires when the data cache has been cleared.
13382          * @param {Store} this
13383          */
13384         clear : true,
13385         /**
13386          * @event beforeload
13387          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13388          * the load action will be canceled.
13389          * @param {Store} this
13390          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13391          */
13392         beforeload : true,
13393         /**
13394          * @event beforeloadadd
13395          * Fires after a new set of Records has been loaded.
13396          * @param {Store} this
13397          * @param {Roo.data.Record[]} records The Records that were loaded
13398          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13399          */
13400         beforeloadadd : true,
13401         /**
13402          * @event load
13403          * Fires after a new set of Records has been loaded, before they are added to the store.
13404          * @param {Store} this
13405          * @param {Roo.data.Record[]} records The Records that were loaded
13406          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13407          * @params {Object} return from reader
13408          */
13409         load : true,
13410         /**
13411          * @event loadexception
13412          * Fires if an exception occurs in the Proxy during loading.
13413          * Called with the signature of the Proxy's "loadexception" event.
13414          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13415          * 
13416          * @param {Proxy} 
13417          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13418          * @param {Object} load options 
13419          * @param {Object} jsonData from your request (normally this contains the Exception)
13420          */
13421         loadexception : true
13422     });
13423     
13424     if(this.proxy){
13425         this.proxy = Roo.factory(this.proxy, Roo.data);
13426         this.proxy.xmodule = this.xmodule || false;
13427         this.relayEvents(this.proxy,  ["loadexception"]);
13428     }
13429     this.sortToggle = {};
13430     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13431
13432     Roo.data.Store.superclass.constructor.call(this);
13433
13434     if(this.inlineData){
13435         this.loadData(this.inlineData);
13436         delete this.inlineData;
13437     }
13438 };
13439
13440 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13441      /**
13442     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13443     * without a remote query - used by combo/forms at present.
13444     */
13445     
13446     /**
13447     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13448     */
13449     /**
13450     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13451     */
13452     /**
13453     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13454     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13455     */
13456     /**
13457     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13458     * on any HTTP request
13459     */
13460     /**
13461     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13462     */
13463     /**
13464     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13465     */
13466     multiSort: false,
13467     /**
13468     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13469     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13470     */
13471     remoteSort : false,
13472
13473     /**
13474     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13475      * loaded or when a record is removed. (defaults to false).
13476     */
13477     pruneModifiedRecords : false,
13478
13479     // private
13480     lastOptions : null,
13481
13482     /**
13483      * Add Records to the Store and fires the add event.
13484      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13485      */
13486     add : function(records){
13487         records = [].concat(records);
13488         for(var i = 0, len = records.length; i < len; i++){
13489             records[i].join(this);
13490         }
13491         var index = this.data.length;
13492         this.data.addAll(records);
13493         this.fireEvent("add", this, records, index);
13494     },
13495
13496     /**
13497      * Remove a Record from the Store and fires the remove event.
13498      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13499      */
13500     remove : function(record){
13501         var index = this.data.indexOf(record);
13502         this.data.removeAt(index);
13503  
13504         if(this.pruneModifiedRecords){
13505             this.modified.remove(record);
13506         }
13507         this.fireEvent("remove", this, record, index);
13508     },
13509
13510     /**
13511      * Remove all Records from the Store and fires the clear event.
13512      */
13513     removeAll : function(){
13514         this.data.clear();
13515         if(this.pruneModifiedRecords){
13516             this.modified = [];
13517         }
13518         this.fireEvent("clear", this);
13519     },
13520
13521     /**
13522      * Inserts Records to the Store at the given index and fires the add event.
13523      * @param {Number} index The start index at which to insert the passed Records.
13524      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13525      */
13526     insert : function(index, records){
13527         records = [].concat(records);
13528         for(var i = 0, len = records.length; i < len; i++){
13529             this.data.insert(index, records[i]);
13530             records[i].join(this);
13531         }
13532         this.fireEvent("add", this, records, index);
13533     },
13534
13535     /**
13536      * Get the index within the cache of the passed Record.
13537      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13538      * @return {Number} The index of the passed Record. Returns -1 if not found.
13539      */
13540     indexOf : function(record){
13541         return this.data.indexOf(record);
13542     },
13543
13544     /**
13545      * Get the index within the cache of the Record with the passed id.
13546      * @param {String} id The id of the Record to find.
13547      * @return {Number} The index of the Record. Returns -1 if not found.
13548      */
13549     indexOfId : function(id){
13550         return this.data.indexOfKey(id);
13551     },
13552
13553     /**
13554      * Get the Record with the specified id.
13555      * @param {String} id The id of the Record to find.
13556      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13557      */
13558     getById : function(id){
13559         return this.data.key(id);
13560     },
13561
13562     /**
13563      * Get the Record at the specified index.
13564      * @param {Number} index The index of the Record to find.
13565      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13566      */
13567     getAt : function(index){
13568         return this.data.itemAt(index);
13569     },
13570
13571     /**
13572      * Returns a range of Records between specified indices.
13573      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13574      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13575      * @return {Roo.data.Record[]} An array of Records
13576      */
13577     getRange : function(start, end){
13578         return this.data.getRange(start, end);
13579     },
13580
13581     // private
13582     storeOptions : function(o){
13583         o = Roo.apply({}, o);
13584         delete o.callback;
13585         delete o.scope;
13586         this.lastOptions = o;
13587     },
13588
13589     /**
13590      * Loads the Record cache from the configured Proxy using the configured Reader.
13591      * <p>
13592      * If using remote paging, then the first load call must specify the <em>start</em>
13593      * and <em>limit</em> properties in the options.params property to establish the initial
13594      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13595      * <p>
13596      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13597      * and this call will return before the new data has been loaded. Perform any post-processing
13598      * in a callback function, or in a "load" event handler.</strong>
13599      * <p>
13600      * @param {Object} options An object containing properties which control loading options:<ul>
13601      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13602      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13603      * passed the following arguments:<ul>
13604      * <li>r : Roo.data.Record[]</li>
13605      * <li>options: Options object from the load call</li>
13606      * <li>success: Boolean success indicator</li></ul></li>
13607      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13608      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13609      * </ul>
13610      */
13611     load : function(options){
13612         options = options || {};
13613         if(this.fireEvent("beforeload", this, options) !== false){
13614             this.storeOptions(options);
13615             var p = Roo.apply(options.params || {}, this.baseParams);
13616             // if meta was not loaded from remote source.. try requesting it.
13617             if (!this.reader.metaFromRemote) {
13618                 p._requestMeta = 1;
13619             }
13620             if(this.sortInfo && this.remoteSort){
13621                 var pn = this.paramNames;
13622                 p[pn["sort"]] = this.sortInfo.field;
13623                 p[pn["dir"]] = this.sortInfo.direction;
13624             }
13625             if (this.multiSort) {
13626                 var pn = this.paramNames;
13627                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13628             }
13629             
13630             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13631         }
13632     },
13633
13634     /**
13635      * Reloads the Record cache from the configured Proxy using the configured Reader and
13636      * the options from the last load operation performed.
13637      * @param {Object} options (optional) An object containing properties which may override the options
13638      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13639      * the most recently used options are reused).
13640      */
13641     reload : function(options){
13642         this.load(Roo.applyIf(options||{}, this.lastOptions));
13643     },
13644
13645     // private
13646     // Called as a callback by the Reader during a load operation.
13647     loadRecords : function(o, options, success){
13648         if(!o || success === false){
13649             if(success !== false){
13650                 this.fireEvent("load", this, [], options, o);
13651             }
13652             if(options.callback){
13653                 options.callback.call(options.scope || this, [], options, false);
13654             }
13655             return;
13656         }
13657         // if data returned failure - throw an exception.
13658         if (o.success === false) {
13659             // show a message if no listener is registered.
13660             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13661                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13662             }
13663             // loadmask wil be hooked into this..
13664             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13665             return;
13666         }
13667         var r = o.records, t = o.totalRecords || r.length;
13668         
13669         this.fireEvent("beforeloadadd", this, r, options, o);
13670         
13671         if(!options || options.add !== true){
13672             if(this.pruneModifiedRecords){
13673                 this.modified = [];
13674             }
13675             for(var i = 0, len = r.length; i < len; i++){
13676                 r[i].join(this);
13677             }
13678             if(this.snapshot){
13679                 this.data = this.snapshot;
13680                 delete this.snapshot;
13681             }
13682             this.data.clear();
13683             this.data.addAll(r);
13684             this.totalLength = t;
13685             this.applySort();
13686             this.fireEvent("datachanged", this);
13687         }else{
13688             this.totalLength = Math.max(t, this.data.length+r.length);
13689             this.add(r);
13690         }
13691         
13692         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13693                 
13694             var e = new Roo.data.Record({});
13695
13696             e.set(this.parent.displayField, this.parent.emptyTitle);
13697             e.set(this.parent.valueField, '');
13698
13699             this.insert(0, e);
13700         }
13701             
13702         this.fireEvent("load", this, r, options, o);
13703         if(options.callback){
13704             options.callback.call(options.scope || this, r, options, true);
13705         }
13706     },
13707
13708
13709     /**
13710      * Loads data from a passed data block. A Reader which understands the format of the data
13711      * must have been configured in the constructor.
13712      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13713      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13714      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13715      */
13716     loadData : function(o, append){
13717         var r = this.reader.readRecords(o);
13718         this.loadRecords(r, {add: append}, true);
13719     },
13720     
13721      /**
13722      * using 'cn' the nested child reader read the child array into it's child stores.
13723      * @param {Object} rec The record with a 'children array
13724      */
13725     loadDataFromChildren : function(rec)
13726     {
13727         this.loadData(this.reader.toLoadData(rec));
13728     },
13729     
13730
13731     /**
13732      * Gets the number of cached records.
13733      * <p>
13734      * <em>If using paging, this may not be the total size of the dataset. If the data object
13735      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13736      * the data set size</em>
13737      */
13738     getCount : function(){
13739         return this.data.length || 0;
13740     },
13741
13742     /**
13743      * Gets the total number of records in the dataset as returned by the server.
13744      * <p>
13745      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13746      * the dataset size</em>
13747      */
13748     getTotalCount : function(){
13749         return this.totalLength || 0;
13750     },
13751
13752     /**
13753      * Returns the sort state of the Store as an object with two properties:
13754      * <pre><code>
13755  field {String} The name of the field by which the Records are sorted
13756  direction {String} The sort order, "ASC" or "DESC"
13757      * </code></pre>
13758      */
13759     getSortState : function(){
13760         return this.sortInfo;
13761     },
13762
13763     // private
13764     applySort : function(){
13765         if(this.sortInfo && !this.remoteSort){
13766             var s = this.sortInfo, f = s.field;
13767             var st = this.fields.get(f).sortType;
13768             var fn = function(r1, r2){
13769                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13770                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13771             };
13772             this.data.sort(s.direction, fn);
13773             if(this.snapshot && this.snapshot != this.data){
13774                 this.snapshot.sort(s.direction, fn);
13775             }
13776         }
13777     },
13778
13779     /**
13780      * Sets the default sort column and order to be used by the next load operation.
13781      * @param {String} fieldName The name of the field to sort by.
13782      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13783      */
13784     setDefaultSort : function(field, dir){
13785         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13786     },
13787
13788     /**
13789      * Sort the Records.
13790      * If remote sorting is used, the sort is performed on the server, and the cache is
13791      * reloaded. If local sorting is used, the cache is sorted internally.
13792      * @param {String} fieldName The name of the field to sort by.
13793      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13794      */
13795     sort : function(fieldName, dir){
13796         var f = this.fields.get(fieldName);
13797         if(!dir){
13798             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13799             
13800             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13801                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13802             }else{
13803                 dir = f.sortDir;
13804             }
13805         }
13806         this.sortToggle[f.name] = dir;
13807         this.sortInfo = {field: f.name, direction: dir};
13808         if(!this.remoteSort){
13809             this.applySort();
13810             this.fireEvent("datachanged", this);
13811         }else{
13812             this.load(this.lastOptions);
13813         }
13814     },
13815
13816     /**
13817      * Calls the specified function for each of the Records in the cache.
13818      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13819      * Returning <em>false</em> aborts and exits the iteration.
13820      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13821      */
13822     each : function(fn, scope){
13823         this.data.each(fn, scope);
13824     },
13825
13826     /**
13827      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13828      * (e.g., during paging).
13829      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13830      */
13831     getModifiedRecords : function(){
13832         return this.modified;
13833     },
13834
13835     // private
13836     createFilterFn : function(property, value, anyMatch){
13837         if(!value.exec){ // not a regex
13838             value = String(value);
13839             if(value.length == 0){
13840                 return false;
13841             }
13842             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13843         }
13844         return function(r){
13845             return value.test(r.data[property]);
13846         };
13847     },
13848
13849     /**
13850      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13851      * @param {String} property A field on your records
13852      * @param {Number} start The record index to start at (defaults to 0)
13853      * @param {Number} end The last record index to include (defaults to length - 1)
13854      * @return {Number} The sum
13855      */
13856     sum : function(property, start, end){
13857         var rs = this.data.items, v = 0;
13858         start = start || 0;
13859         end = (end || end === 0) ? end : rs.length-1;
13860
13861         for(var i = start; i <= end; i++){
13862             v += (rs[i].data[property] || 0);
13863         }
13864         return v;
13865     },
13866
13867     /**
13868      * Filter the records by a specified property.
13869      * @param {String} field A field on your records
13870      * @param {String/RegExp} value Either a string that the field
13871      * should start with or a RegExp to test against the field
13872      * @param {Boolean} anyMatch True to match any part not just the beginning
13873      */
13874     filter : function(property, value, anyMatch){
13875         var fn = this.createFilterFn(property, value, anyMatch);
13876         return fn ? this.filterBy(fn) : this.clearFilter();
13877     },
13878
13879     /**
13880      * Filter by a function. The specified function will be called with each
13881      * record in this data source. If the function returns true the record is included,
13882      * otherwise it is filtered.
13883      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13884      * @param {Object} scope (optional) The scope of the function (defaults to this)
13885      */
13886     filterBy : function(fn, scope){
13887         this.snapshot = this.snapshot || this.data;
13888         this.data = this.queryBy(fn, scope||this);
13889         this.fireEvent("datachanged", this);
13890     },
13891
13892     /**
13893      * Query the records by a specified property.
13894      * @param {String} field A field on your records
13895      * @param {String/RegExp} value Either a string that the field
13896      * should start with or a RegExp to test against the field
13897      * @param {Boolean} anyMatch True to match any part not just the beginning
13898      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13899      */
13900     query : function(property, value, anyMatch){
13901         var fn = this.createFilterFn(property, value, anyMatch);
13902         return fn ? this.queryBy(fn) : this.data.clone();
13903     },
13904
13905     /**
13906      * Query by a function. The specified function will be called with each
13907      * record in this data source. If the function returns true the record is included
13908      * in the results.
13909      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13910      * @param {Object} scope (optional) The scope of the function (defaults to this)
13911       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13912      **/
13913     queryBy : function(fn, scope){
13914         var data = this.snapshot || this.data;
13915         return data.filterBy(fn, scope||this);
13916     },
13917
13918     /**
13919      * Collects unique values for a particular dataIndex from this store.
13920      * @param {String} dataIndex The property to collect
13921      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13922      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13923      * @return {Array} An array of the unique values
13924      **/
13925     collect : function(dataIndex, allowNull, bypassFilter){
13926         var d = (bypassFilter === true && this.snapshot) ?
13927                 this.snapshot.items : this.data.items;
13928         var v, sv, r = [], l = {};
13929         for(var i = 0, len = d.length; i < len; i++){
13930             v = d[i].data[dataIndex];
13931             sv = String(v);
13932             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13933                 l[sv] = true;
13934                 r[r.length] = v;
13935             }
13936         }
13937         return r;
13938     },
13939
13940     /**
13941      * Revert to a view of the Record cache with no filtering applied.
13942      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13943      */
13944     clearFilter : function(suppressEvent){
13945         if(this.snapshot && this.snapshot != this.data){
13946             this.data = this.snapshot;
13947             delete this.snapshot;
13948             if(suppressEvent !== true){
13949                 this.fireEvent("datachanged", this);
13950             }
13951         }
13952     },
13953
13954     // private
13955     afterEdit : function(record){
13956         if(this.modified.indexOf(record) == -1){
13957             this.modified.push(record);
13958         }
13959         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13960     },
13961     
13962     // private
13963     afterReject : function(record){
13964         this.modified.remove(record);
13965         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13966     },
13967
13968     // private
13969     afterCommit : function(record){
13970         this.modified.remove(record);
13971         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13972     },
13973
13974     /**
13975      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13976      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13977      */
13978     commitChanges : function(){
13979         var m = this.modified.slice(0);
13980         this.modified = [];
13981         for(var i = 0, len = m.length; i < len; i++){
13982             m[i].commit();
13983         }
13984     },
13985
13986     /**
13987      * Cancel outstanding changes on all changed records.
13988      */
13989     rejectChanges : function(){
13990         var m = this.modified.slice(0);
13991         this.modified = [];
13992         for(var i = 0, len = m.length; i < len; i++){
13993             m[i].reject();
13994         }
13995     },
13996
13997     onMetaChange : function(meta, rtype, o){
13998         this.recordType = rtype;
13999         this.fields = rtype.prototype.fields;
14000         delete this.snapshot;
14001         this.sortInfo = meta.sortInfo || this.sortInfo;
14002         this.modified = [];
14003         this.fireEvent('metachange', this, this.reader.meta);
14004     },
14005     
14006     moveIndex : function(data, type)
14007     {
14008         var index = this.indexOf(data);
14009         
14010         var newIndex = index + type;
14011         
14012         this.remove(data);
14013         
14014         this.insert(newIndex, data);
14015         
14016     }
14017 });/*
14018  * Based on:
14019  * Ext JS Library 1.1.1
14020  * Copyright(c) 2006-2007, Ext JS, LLC.
14021  *
14022  * Originally Released Under LGPL - original licence link has changed is not relivant.
14023  *
14024  * Fork - LGPL
14025  * <script type="text/javascript">
14026  */
14027
14028 /**
14029  * @class Roo.data.SimpleStore
14030  * @extends Roo.data.Store
14031  * Small helper class to make creating Stores from Array data easier.
14032  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14033  * @cfg {Array} fields An array of field definition objects, or field name strings.
14034  * @cfg {Object} an existing reader (eg. copied from another store)
14035  * @cfg {Array} data The multi-dimensional array of data
14036  * @constructor
14037  * @param {Object} config
14038  */
14039 Roo.data.SimpleStore = function(config)
14040 {
14041     Roo.data.SimpleStore.superclass.constructor.call(this, {
14042         isLocal : true,
14043         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14044                 id: config.id
14045             },
14046             Roo.data.Record.create(config.fields)
14047         ),
14048         proxy : new Roo.data.MemoryProxy(config.data)
14049     });
14050     this.load();
14051 };
14052 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14053  * Based on:
14054  * Ext JS Library 1.1.1
14055  * Copyright(c) 2006-2007, Ext JS, LLC.
14056  *
14057  * Originally Released Under LGPL - original licence link has changed is not relivant.
14058  *
14059  * Fork - LGPL
14060  * <script type="text/javascript">
14061  */
14062
14063 /**
14064 /**
14065  * @extends Roo.data.Store
14066  * @class Roo.data.JsonStore
14067  * Small helper class to make creating Stores for JSON data easier. <br/>
14068 <pre><code>
14069 var store = new Roo.data.JsonStore({
14070     url: 'get-images.php',
14071     root: 'images',
14072     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14073 });
14074 </code></pre>
14075  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14076  * JsonReader and HttpProxy (unless inline data is provided).</b>
14077  * @cfg {Array} fields An array of field definition objects, or field name strings.
14078  * @constructor
14079  * @param {Object} config
14080  */
14081 Roo.data.JsonStore = function(c){
14082     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14083         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14084         reader: new Roo.data.JsonReader(c, c.fields)
14085     }));
14086 };
14087 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14088  * Based on:
14089  * Ext JS Library 1.1.1
14090  * Copyright(c) 2006-2007, Ext JS, LLC.
14091  *
14092  * Originally Released Under LGPL - original licence link has changed is not relivant.
14093  *
14094  * Fork - LGPL
14095  * <script type="text/javascript">
14096  */
14097
14098  
14099 Roo.data.Field = function(config){
14100     if(typeof config == "string"){
14101         config = {name: config};
14102     }
14103     Roo.apply(this, config);
14104     
14105     if(!this.type){
14106         this.type = "auto";
14107     }
14108     
14109     var st = Roo.data.SortTypes;
14110     // named sortTypes are supported, here we look them up
14111     if(typeof this.sortType == "string"){
14112         this.sortType = st[this.sortType];
14113     }
14114     
14115     // set default sortType for strings and dates
14116     if(!this.sortType){
14117         switch(this.type){
14118             case "string":
14119                 this.sortType = st.asUCString;
14120                 break;
14121             case "date":
14122                 this.sortType = st.asDate;
14123                 break;
14124             default:
14125                 this.sortType = st.none;
14126         }
14127     }
14128
14129     // define once
14130     var stripRe = /[\$,%]/g;
14131
14132     // prebuilt conversion function for this field, instead of
14133     // switching every time we're reading a value
14134     if(!this.convert){
14135         var cv, dateFormat = this.dateFormat;
14136         switch(this.type){
14137             case "":
14138             case "auto":
14139             case undefined:
14140                 cv = function(v){ return v; };
14141                 break;
14142             case "string":
14143                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14144                 break;
14145             case "int":
14146                 cv = function(v){
14147                     return v !== undefined && v !== null && v !== '' ?
14148                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14149                     };
14150                 break;
14151             case "float":
14152                 cv = function(v){
14153                     return v !== undefined && v !== null && v !== '' ?
14154                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14155                     };
14156                 break;
14157             case "bool":
14158             case "boolean":
14159                 cv = function(v){ return v === true || v === "true" || v == 1; };
14160                 break;
14161             case "date":
14162                 cv = function(v){
14163                     if(!v){
14164                         return '';
14165                     }
14166                     if(v instanceof Date){
14167                         return v;
14168                     }
14169                     if(dateFormat){
14170                         if(dateFormat == "timestamp"){
14171                             return new Date(v*1000);
14172                         }
14173                         return Date.parseDate(v, dateFormat);
14174                     }
14175                     var parsed = Date.parse(v);
14176                     return parsed ? new Date(parsed) : null;
14177                 };
14178              break;
14179             
14180         }
14181         this.convert = cv;
14182     }
14183 };
14184
14185 Roo.data.Field.prototype = {
14186     dateFormat: null,
14187     defaultValue: "",
14188     mapping: null,
14189     sortType : null,
14190     sortDir : "ASC"
14191 };/*
14192  * Based on:
14193  * Ext JS Library 1.1.1
14194  * Copyright(c) 2006-2007, Ext JS, LLC.
14195  *
14196  * Originally Released Under LGPL - original licence link has changed is not relivant.
14197  *
14198  * Fork - LGPL
14199  * <script type="text/javascript">
14200  */
14201  
14202 // Base class for reading structured data from a data source.  This class is intended to be
14203 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14204
14205 /**
14206  * @class Roo.data.DataReader
14207  * Base class for reading structured data from a data source.  This class is intended to be
14208  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14209  */
14210
14211 Roo.data.DataReader = function(meta, recordType){
14212     
14213     this.meta = meta;
14214     
14215     this.recordType = recordType instanceof Array ? 
14216         Roo.data.Record.create(recordType) : recordType;
14217 };
14218
14219 Roo.data.DataReader.prototype = {
14220     
14221     
14222     readerType : 'Data',
14223      /**
14224      * Create an empty record
14225      * @param {Object} data (optional) - overlay some values
14226      * @return {Roo.data.Record} record created.
14227      */
14228     newRow :  function(d) {
14229         var da =  {};
14230         this.recordType.prototype.fields.each(function(c) {
14231             switch( c.type) {
14232                 case 'int' : da[c.name] = 0; break;
14233                 case 'date' : da[c.name] = new Date(); break;
14234                 case 'float' : da[c.name] = 0.0; break;
14235                 case 'boolean' : da[c.name] = false; break;
14236                 default : da[c.name] = ""; break;
14237             }
14238             
14239         });
14240         return new this.recordType(Roo.apply(da, d));
14241     }
14242     
14243     
14244 };/*
14245  * Based on:
14246  * Ext JS Library 1.1.1
14247  * Copyright(c) 2006-2007, Ext JS, LLC.
14248  *
14249  * Originally Released Under LGPL - original licence link has changed is not relivant.
14250  *
14251  * Fork - LGPL
14252  * <script type="text/javascript">
14253  */
14254
14255 /**
14256  * @class Roo.data.DataProxy
14257  * @extends Roo.data.Observable
14258  * This class is an abstract base class for implementations which provide retrieval of
14259  * unformatted data objects.<br>
14260  * <p>
14261  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14262  * (of the appropriate type which knows how to parse the data object) to provide a block of
14263  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14264  * <p>
14265  * Custom implementations must implement the load method as described in
14266  * {@link Roo.data.HttpProxy#load}.
14267  */
14268 Roo.data.DataProxy = function(){
14269     this.addEvents({
14270         /**
14271          * @event beforeload
14272          * Fires before a network request is made to retrieve a data object.
14273          * @param {Object} This DataProxy object.
14274          * @param {Object} params The params parameter to the load function.
14275          */
14276         beforeload : true,
14277         /**
14278          * @event load
14279          * Fires before the load method's callback is called.
14280          * @param {Object} This DataProxy object.
14281          * @param {Object} o The data object.
14282          * @param {Object} arg The callback argument object passed to the load function.
14283          */
14284         load : true,
14285         /**
14286          * @event loadexception
14287          * Fires if an Exception occurs during data retrieval.
14288          * @param {Object} This DataProxy object.
14289          * @param {Object} o The data object.
14290          * @param {Object} arg The callback argument object passed to the load function.
14291          * @param {Object} e The Exception.
14292          */
14293         loadexception : true
14294     });
14295     Roo.data.DataProxy.superclass.constructor.call(this);
14296 };
14297
14298 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14299
14300     /**
14301      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14302      */
14303 /*
14304  * Based on:
14305  * Ext JS Library 1.1.1
14306  * Copyright(c) 2006-2007, Ext JS, LLC.
14307  *
14308  * Originally Released Under LGPL - original licence link has changed is not relivant.
14309  *
14310  * Fork - LGPL
14311  * <script type="text/javascript">
14312  */
14313 /**
14314  * @class Roo.data.MemoryProxy
14315  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14316  * to the Reader when its load method is called.
14317  * @constructor
14318  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14319  */
14320 Roo.data.MemoryProxy = function(data){
14321     if (data.data) {
14322         data = data.data;
14323     }
14324     Roo.data.MemoryProxy.superclass.constructor.call(this);
14325     this.data = data;
14326 };
14327
14328 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14329     
14330     /**
14331      * Load data from the requested source (in this case an in-memory
14332      * data object passed to the constructor), read the data object into
14333      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14334      * process that block using the passed callback.
14335      * @param {Object} params This parameter is not used by the MemoryProxy class.
14336      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14337      * object into a block of Roo.data.Records.
14338      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14339      * The function must be passed <ul>
14340      * <li>The Record block object</li>
14341      * <li>The "arg" argument from the load function</li>
14342      * <li>A boolean success indicator</li>
14343      * </ul>
14344      * @param {Object} scope The scope in which to call the callback
14345      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14346      */
14347     load : function(params, reader, callback, scope, arg){
14348         params = params || {};
14349         var result;
14350         try {
14351             result = reader.readRecords(params.data ? params.data :this.data);
14352         }catch(e){
14353             this.fireEvent("loadexception", this, arg, null, e);
14354             callback.call(scope, null, arg, false);
14355             return;
14356         }
14357         callback.call(scope, result, arg, true);
14358     },
14359     
14360     // private
14361     update : function(params, records){
14362         
14363     }
14364 });/*
14365  * Based on:
14366  * Ext JS Library 1.1.1
14367  * Copyright(c) 2006-2007, Ext JS, LLC.
14368  *
14369  * Originally Released Under LGPL - original licence link has changed is not relivant.
14370  *
14371  * Fork - LGPL
14372  * <script type="text/javascript">
14373  */
14374 /**
14375  * @class Roo.data.HttpProxy
14376  * @extends Roo.data.DataProxy
14377  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14378  * configured to reference a certain URL.<br><br>
14379  * <p>
14380  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14381  * from which the running page was served.<br><br>
14382  * <p>
14383  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14384  * <p>
14385  * Be aware that to enable the browser to parse an XML document, the server must set
14386  * the Content-Type header in the HTTP response to "text/xml".
14387  * @constructor
14388  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14389  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14390  * will be used to make the request.
14391  */
14392 Roo.data.HttpProxy = function(conn){
14393     Roo.data.HttpProxy.superclass.constructor.call(this);
14394     // is conn a conn config or a real conn?
14395     this.conn = conn;
14396     this.useAjax = !conn || !conn.events;
14397   
14398 };
14399
14400 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14401     // thse are take from connection...
14402     
14403     /**
14404      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14405      */
14406     /**
14407      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14408      * extra parameters to each request made by this object. (defaults to undefined)
14409      */
14410     /**
14411      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14412      *  to each request made by this object. (defaults to undefined)
14413      */
14414     /**
14415      * @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)
14416      */
14417     /**
14418      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14419      */
14420      /**
14421      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14422      * @type Boolean
14423      */
14424   
14425
14426     /**
14427      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14428      * @type Boolean
14429      */
14430     /**
14431      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14432      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14433      * a finer-grained basis than the DataProxy events.
14434      */
14435     getConnection : function(){
14436         return this.useAjax ? Roo.Ajax : this.conn;
14437     },
14438
14439     /**
14440      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14441      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14442      * process that block using the passed callback.
14443      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14444      * for the request to the remote server.
14445      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14446      * object into a block of Roo.data.Records.
14447      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14448      * The function must be passed <ul>
14449      * <li>The Record block object</li>
14450      * <li>The "arg" argument from the load function</li>
14451      * <li>A boolean success indicator</li>
14452      * </ul>
14453      * @param {Object} scope The scope in which to call the callback
14454      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14455      */
14456     load : function(params, reader, callback, scope, arg){
14457         if(this.fireEvent("beforeload", this, params) !== false){
14458             var  o = {
14459                 params : params || {},
14460                 request: {
14461                     callback : callback,
14462                     scope : scope,
14463                     arg : arg
14464                 },
14465                 reader: reader,
14466                 callback : this.loadResponse,
14467                 scope: this
14468             };
14469             if(this.useAjax){
14470                 Roo.applyIf(o, this.conn);
14471                 if(this.activeRequest){
14472                     Roo.Ajax.abort(this.activeRequest);
14473                 }
14474                 this.activeRequest = Roo.Ajax.request(o);
14475             }else{
14476                 this.conn.request(o);
14477             }
14478         }else{
14479             callback.call(scope||this, null, arg, false);
14480         }
14481     },
14482
14483     // private
14484     loadResponse : function(o, success, response){
14485         delete this.activeRequest;
14486         if(!success){
14487             this.fireEvent("loadexception", this, o, response);
14488             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14489             return;
14490         }
14491         var result;
14492         try {
14493             result = o.reader.read(response);
14494         }catch(e){
14495             this.fireEvent("loadexception", this, o, response, e);
14496             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14497             return;
14498         }
14499         
14500         this.fireEvent("load", this, o, o.request.arg);
14501         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14502     },
14503
14504     // private
14505     update : function(dataSet){
14506
14507     },
14508
14509     // private
14510     updateResponse : function(dataSet){
14511
14512     }
14513 });/*
14514  * Based on:
14515  * Ext JS Library 1.1.1
14516  * Copyright(c) 2006-2007, Ext JS, LLC.
14517  *
14518  * Originally Released Under LGPL - original licence link has changed is not relivant.
14519  *
14520  * Fork - LGPL
14521  * <script type="text/javascript">
14522  */
14523
14524 /**
14525  * @class Roo.data.ScriptTagProxy
14526  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14527  * other than the originating domain of the running page.<br><br>
14528  * <p>
14529  * <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
14530  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14531  * <p>
14532  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14533  * source code that is used as the source inside a &lt;script> tag.<br><br>
14534  * <p>
14535  * In order for the browser to process the returned data, the server must wrap the data object
14536  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14537  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14538  * depending on whether the callback name was passed:
14539  * <p>
14540  * <pre><code>
14541 boolean scriptTag = false;
14542 String cb = request.getParameter("callback");
14543 if (cb != null) {
14544     scriptTag = true;
14545     response.setContentType("text/javascript");
14546 } else {
14547     response.setContentType("application/x-json");
14548 }
14549 Writer out = response.getWriter();
14550 if (scriptTag) {
14551     out.write(cb + "(");
14552 }
14553 out.print(dataBlock.toJsonString());
14554 if (scriptTag) {
14555     out.write(");");
14556 }
14557 </pre></code>
14558  *
14559  * @constructor
14560  * @param {Object} config A configuration object.
14561  */
14562 Roo.data.ScriptTagProxy = function(config){
14563     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14564     Roo.apply(this, config);
14565     this.head = document.getElementsByTagName("head")[0];
14566 };
14567
14568 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14569
14570 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14571     /**
14572      * @cfg {String} url The URL from which to request the data object.
14573      */
14574     /**
14575      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14576      */
14577     timeout : 30000,
14578     /**
14579      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14580      * the server the name of the callback function set up by the load call to process the returned data object.
14581      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14582      * javascript output which calls this named function passing the data object as its only parameter.
14583      */
14584     callbackParam : "callback",
14585     /**
14586      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14587      * name to the request.
14588      */
14589     nocache : true,
14590
14591     /**
14592      * Load data from the configured URL, read the data object into
14593      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14594      * process that block using the passed callback.
14595      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14596      * for the request to the remote server.
14597      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14598      * object into a block of Roo.data.Records.
14599      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14600      * The function must be passed <ul>
14601      * <li>The Record block object</li>
14602      * <li>The "arg" argument from the load function</li>
14603      * <li>A boolean success indicator</li>
14604      * </ul>
14605      * @param {Object} scope The scope in which to call the callback
14606      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14607      */
14608     load : function(params, reader, callback, scope, arg){
14609         if(this.fireEvent("beforeload", this, params) !== false){
14610
14611             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14612
14613             var url = this.url;
14614             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14615             if(this.nocache){
14616                 url += "&_dc=" + (new Date().getTime());
14617             }
14618             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14619             var trans = {
14620                 id : transId,
14621                 cb : "stcCallback"+transId,
14622                 scriptId : "stcScript"+transId,
14623                 params : params,
14624                 arg : arg,
14625                 url : url,
14626                 callback : callback,
14627                 scope : scope,
14628                 reader : reader
14629             };
14630             var conn = this;
14631
14632             window[trans.cb] = function(o){
14633                 conn.handleResponse(o, trans);
14634             };
14635
14636             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14637
14638             if(this.autoAbort !== false){
14639                 this.abort();
14640             }
14641
14642             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14643
14644             var script = document.createElement("script");
14645             script.setAttribute("src", url);
14646             script.setAttribute("type", "text/javascript");
14647             script.setAttribute("id", trans.scriptId);
14648             this.head.appendChild(script);
14649
14650             this.trans = trans;
14651         }else{
14652             callback.call(scope||this, null, arg, false);
14653         }
14654     },
14655
14656     // private
14657     isLoading : function(){
14658         return this.trans ? true : false;
14659     },
14660
14661     /**
14662      * Abort the current server request.
14663      */
14664     abort : function(){
14665         if(this.isLoading()){
14666             this.destroyTrans(this.trans);
14667         }
14668     },
14669
14670     // private
14671     destroyTrans : function(trans, isLoaded){
14672         this.head.removeChild(document.getElementById(trans.scriptId));
14673         clearTimeout(trans.timeoutId);
14674         if(isLoaded){
14675             window[trans.cb] = undefined;
14676             try{
14677                 delete window[trans.cb];
14678             }catch(e){}
14679         }else{
14680             // if hasn't been loaded, wait for load to remove it to prevent script error
14681             window[trans.cb] = function(){
14682                 window[trans.cb] = undefined;
14683                 try{
14684                     delete window[trans.cb];
14685                 }catch(e){}
14686             };
14687         }
14688     },
14689
14690     // private
14691     handleResponse : function(o, trans){
14692         this.trans = false;
14693         this.destroyTrans(trans, true);
14694         var result;
14695         try {
14696             result = trans.reader.readRecords(o);
14697         }catch(e){
14698             this.fireEvent("loadexception", this, o, trans.arg, e);
14699             trans.callback.call(trans.scope||window, null, trans.arg, false);
14700             return;
14701         }
14702         this.fireEvent("load", this, o, trans.arg);
14703         trans.callback.call(trans.scope||window, result, trans.arg, true);
14704     },
14705
14706     // private
14707     handleFailure : function(trans){
14708         this.trans = false;
14709         this.destroyTrans(trans, false);
14710         this.fireEvent("loadexception", this, null, trans.arg);
14711         trans.callback.call(trans.scope||window, null, trans.arg, false);
14712     }
14713 });/*
14714  * Based on:
14715  * Ext JS Library 1.1.1
14716  * Copyright(c) 2006-2007, Ext JS, LLC.
14717  *
14718  * Originally Released Under LGPL - original licence link has changed is not relivant.
14719  *
14720  * Fork - LGPL
14721  * <script type="text/javascript">
14722  */
14723
14724 /**
14725  * @class Roo.data.JsonReader
14726  * @extends Roo.data.DataReader
14727  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14728  * based on mappings in a provided Roo.data.Record constructor.
14729  * 
14730  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14731  * in the reply previously. 
14732  * 
14733  * <p>
14734  * Example code:
14735  * <pre><code>
14736 var RecordDef = Roo.data.Record.create([
14737     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14738     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14739 ]);
14740 var myReader = new Roo.data.JsonReader({
14741     totalProperty: "results",    // The property which contains the total dataset size (optional)
14742     root: "rows",                // The property which contains an Array of row objects
14743     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14744 }, RecordDef);
14745 </code></pre>
14746  * <p>
14747  * This would consume a JSON file like this:
14748  * <pre><code>
14749 { 'results': 2, 'rows': [
14750     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14751     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14752 }
14753 </code></pre>
14754  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14755  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14756  * paged from the remote server.
14757  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14758  * @cfg {String} root name of the property which contains the Array of row objects.
14759  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14760  * @cfg {Array} fields Array of field definition objects
14761  * @constructor
14762  * Create a new JsonReader
14763  * @param {Object} meta Metadata configuration options
14764  * @param {Object} recordType Either an Array of field definition objects,
14765  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14766  */
14767 Roo.data.JsonReader = function(meta, recordType){
14768     
14769     meta = meta || {};
14770     // set some defaults:
14771     Roo.applyIf(meta, {
14772         totalProperty: 'total',
14773         successProperty : 'success',
14774         root : 'data',
14775         id : 'id'
14776     });
14777     
14778     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14779 };
14780 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14781     
14782     readerType : 'Json',
14783     
14784     /**
14785      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14786      * Used by Store query builder to append _requestMeta to params.
14787      * 
14788      */
14789     metaFromRemote : false,
14790     /**
14791      * This method is only used by a DataProxy which has retrieved data from a remote server.
14792      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14793      * @return {Object} data A data block which is used by an Roo.data.Store object as
14794      * a cache of Roo.data.Records.
14795      */
14796     read : function(response){
14797         var json = response.responseText;
14798        
14799         var o = /* eval:var:o */ eval("("+json+")");
14800         if(!o) {
14801             throw {message: "JsonReader.read: Json object not found"};
14802         }
14803         
14804         if(o.metaData){
14805             
14806             delete this.ef;
14807             this.metaFromRemote = true;
14808             this.meta = o.metaData;
14809             this.recordType = Roo.data.Record.create(o.metaData.fields);
14810             this.onMetaChange(this.meta, this.recordType, o);
14811         }
14812         return this.readRecords(o);
14813     },
14814
14815     // private function a store will implement
14816     onMetaChange : function(meta, recordType, o){
14817
14818     },
14819
14820     /**
14821          * @ignore
14822          */
14823     simpleAccess: function(obj, subsc) {
14824         return obj[subsc];
14825     },
14826
14827         /**
14828          * @ignore
14829          */
14830     getJsonAccessor: function(){
14831         var re = /[\[\.]/;
14832         return function(expr) {
14833             try {
14834                 return(re.test(expr))
14835                     ? new Function("obj", "return obj." + expr)
14836                     : function(obj){
14837                         return obj[expr];
14838                     };
14839             } catch(e){}
14840             return Roo.emptyFn;
14841         };
14842     }(),
14843
14844     /**
14845      * Create a data block containing Roo.data.Records from an XML document.
14846      * @param {Object} o An object which contains an Array of row objects in the property specified
14847      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14848      * which contains the total size of the dataset.
14849      * @return {Object} data A data block which is used by an Roo.data.Store object as
14850      * a cache of Roo.data.Records.
14851      */
14852     readRecords : function(o){
14853         /**
14854          * After any data loads, the raw JSON data is available for further custom processing.
14855          * @type Object
14856          */
14857         this.o = o;
14858         var s = this.meta, Record = this.recordType,
14859             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14860
14861 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14862         if (!this.ef) {
14863             if(s.totalProperty) {
14864                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14865                 }
14866                 if(s.successProperty) {
14867                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14868                 }
14869                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14870                 if (s.id) {
14871                         var g = this.getJsonAccessor(s.id);
14872                         this.getId = function(rec) {
14873                                 var r = g(rec);  
14874                                 return (r === undefined || r === "") ? null : r;
14875                         };
14876                 } else {
14877                         this.getId = function(){return null;};
14878                 }
14879             this.ef = [];
14880             for(var jj = 0; jj < fl; jj++){
14881                 f = fi[jj];
14882                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14883                 this.ef[jj] = this.getJsonAccessor(map);
14884             }
14885         }
14886
14887         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14888         if(s.totalProperty){
14889             var vt = parseInt(this.getTotal(o), 10);
14890             if(!isNaN(vt)){
14891                 totalRecords = vt;
14892             }
14893         }
14894         if(s.successProperty){
14895             var vs = this.getSuccess(o);
14896             if(vs === false || vs === 'false'){
14897                 success = false;
14898             }
14899         }
14900         var records = [];
14901         for(var i = 0; i < c; i++){
14902                 var n = root[i];
14903             var values = {};
14904             var id = this.getId(n);
14905             for(var j = 0; j < fl; j++){
14906                 f = fi[j];
14907             var v = this.ef[j](n);
14908             if (!f.convert) {
14909                 Roo.log('missing convert for ' + f.name);
14910                 Roo.log(f);
14911                 continue;
14912             }
14913             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14914             }
14915             var record = new Record(values, id);
14916             record.json = n;
14917             records[i] = record;
14918         }
14919         return {
14920             raw : o,
14921             success : success,
14922             records : records,
14923             totalRecords : totalRecords
14924         };
14925     },
14926     // used when loading children.. @see loadDataFromChildren
14927     toLoadData: function(rec)
14928     {
14929         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14930         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14931         return { data : data, total : data.length };
14932         
14933     }
14934 });/*
14935  * Based on:
14936  * Ext JS Library 1.1.1
14937  * Copyright(c) 2006-2007, Ext JS, LLC.
14938  *
14939  * Originally Released Under LGPL - original licence link has changed is not relivant.
14940  *
14941  * Fork - LGPL
14942  * <script type="text/javascript">
14943  */
14944
14945 /**
14946  * @class Roo.data.ArrayReader
14947  * @extends Roo.data.DataReader
14948  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14949  * Each element of that Array represents a row of data fields. The
14950  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14951  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14952  * <p>
14953  * Example code:.
14954  * <pre><code>
14955 var RecordDef = Roo.data.Record.create([
14956     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14957     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14958 ]);
14959 var myReader = new Roo.data.ArrayReader({
14960     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14961 }, RecordDef);
14962 </code></pre>
14963  * <p>
14964  * This would consume an Array like this:
14965  * <pre><code>
14966 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14967   </code></pre>
14968  
14969  * @constructor
14970  * Create a new JsonReader
14971  * @param {Object} meta Metadata configuration options.
14972  * @param {Object|Array} recordType Either an Array of field definition objects
14973  * 
14974  * @cfg {Array} fields Array of field definition objects
14975  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14976  * as specified to {@link Roo.data.Record#create},
14977  * or an {@link Roo.data.Record} object
14978  *
14979  * 
14980  * created using {@link Roo.data.Record#create}.
14981  */
14982 Roo.data.ArrayReader = function(meta, recordType)
14983 {    
14984     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14985 };
14986
14987 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14988     
14989       /**
14990      * Create a data block containing Roo.data.Records from an XML document.
14991      * @param {Object} o An Array of row objects which represents the dataset.
14992      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14993      * a cache of Roo.data.Records.
14994      */
14995     readRecords : function(o)
14996     {
14997         var sid = this.meta ? this.meta.id : null;
14998         var recordType = this.recordType, fields = recordType.prototype.fields;
14999         var records = [];
15000         var root = o;
15001         for(var i = 0; i < root.length; i++){
15002                 var n = root[i];
15003             var values = {};
15004             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15005             for(var j = 0, jlen = fields.length; j < jlen; j++){
15006                 var f = fields.items[j];
15007                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15008                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15009                 v = f.convert(v);
15010                 values[f.name] = v;
15011             }
15012             var record = new recordType(values, id);
15013             record.json = n;
15014             records[records.length] = record;
15015         }
15016         return {
15017             records : records,
15018             totalRecords : records.length
15019         };
15020     },
15021     // used when loading children.. @see loadDataFromChildren
15022     toLoadData: function(rec)
15023     {
15024         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15025         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15026         
15027     }
15028     
15029     
15030 });/*
15031  * - LGPL
15032  * * 
15033  */
15034
15035 /**
15036  * @class Roo.bootstrap.ComboBox
15037  * @extends Roo.bootstrap.TriggerField
15038  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15039  * @cfg {Boolean} append (true|false) default false
15040  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15041  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15042  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15043  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15044  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15045  * @cfg {Boolean} animate default true
15046  * @cfg {Boolean} emptyResultText only for touch device
15047  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15048  * @cfg {String} emptyTitle default ''
15049  * @cfg {Number} width fixed with? experimental
15050  * @constructor
15051  * Create a new ComboBox.
15052  * @param {Object} config Configuration options
15053  */
15054 Roo.bootstrap.ComboBox = function(config){
15055     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15056     this.addEvents({
15057         /**
15058          * @event expand
15059          * Fires when the dropdown list is expanded
15060         * @param {Roo.bootstrap.ComboBox} combo This combo box
15061         */
15062         'expand' : true,
15063         /**
15064          * @event collapse
15065          * Fires when the dropdown list is collapsed
15066         * @param {Roo.bootstrap.ComboBox} combo This combo box
15067         */
15068         'collapse' : true,
15069         /**
15070          * @event beforeselect
15071          * Fires before a list item is selected. Return false to cancel the selection.
15072         * @param {Roo.bootstrap.ComboBox} combo This combo box
15073         * @param {Roo.data.Record} record The data record returned from the underlying store
15074         * @param {Number} index The index of the selected item in the dropdown list
15075         */
15076         'beforeselect' : true,
15077         /**
15078          * @event select
15079          * Fires when a list item is selected
15080         * @param {Roo.bootstrap.ComboBox} combo This combo box
15081         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15082         * @param {Number} index The index of the selected item in the dropdown list
15083         */
15084         'select' : true,
15085         /**
15086          * @event beforequery
15087          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15088          * The event object passed has these properties:
15089         * @param {Roo.bootstrap.ComboBox} combo This combo box
15090         * @param {String} query The query
15091         * @param {Boolean} forceAll true to force "all" query
15092         * @param {Boolean} cancel true to cancel the query
15093         * @param {Object} e The query event object
15094         */
15095         'beforequery': true,
15096          /**
15097          * @event add
15098          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15099         * @param {Roo.bootstrap.ComboBox} combo This combo box
15100         */
15101         'add' : true,
15102         /**
15103          * @event edit
15104          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15105         * @param {Roo.bootstrap.ComboBox} combo This combo box
15106         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15107         */
15108         'edit' : true,
15109         /**
15110          * @event remove
15111          * Fires when the remove value from the combobox array
15112         * @param {Roo.bootstrap.ComboBox} combo This combo box
15113         */
15114         'remove' : true,
15115         /**
15116          * @event afterremove
15117          * Fires when the remove value from the combobox array
15118         * @param {Roo.bootstrap.ComboBox} combo This combo box
15119         */
15120         'afterremove' : true,
15121         /**
15122          * @event specialfilter
15123          * Fires when specialfilter
15124             * @param {Roo.bootstrap.ComboBox} combo This combo box
15125             */
15126         'specialfilter' : true,
15127         /**
15128          * @event tick
15129          * Fires when tick the element
15130             * @param {Roo.bootstrap.ComboBox} combo This combo box
15131             */
15132         'tick' : true,
15133         /**
15134          * @event touchviewdisplay
15135          * Fires when touch view require special display (default is using displayField)
15136             * @param {Roo.bootstrap.ComboBox} combo This combo box
15137             * @param {Object} cfg set html .
15138             */
15139         'touchviewdisplay' : true
15140         
15141     });
15142     
15143     this.item = [];
15144     this.tickItems = [];
15145     
15146     this.selectedIndex = -1;
15147     if(this.mode == 'local'){
15148         if(config.queryDelay === undefined){
15149             this.queryDelay = 10;
15150         }
15151         if(config.minChars === undefined){
15152             this.minChars = 0;
15153         }
15154     }
15155 };
15156
15157 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15158      
15159     /**
15160      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15161      * rendering into an Roo.Editor, defaults to false)
15162      */
15163     /**
15164      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15165      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15166      */
15167     /**
15168      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15169      */
15170     /**
15171      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15172      * the dropdown list (defaults to undefined, with no header element)
15173      */
15174
15175      /**
15176      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15177      */
15178      
15179      /**
15180      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15181      */
15182     listWidth: undefined,
15183     /**
15184      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15185      * mode = 'remote' or 'text' if mode = 'local')
15186      */
15187     displayField: undefined,
15188     
15189     /**
15190      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15191      * mode = 'remote' or 'value' if mode = 'local'). 
15192      * Note: use of a valueField requires the user make a selection
15193      * in order for a value to be mapped.
15194      */
15195     valueField: undefined,
15196     /**
15197      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15198      */
15199     modalTitle : '',
15200     
15201     /**
15202      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15203      * field's data value (defaults to the underlying DOM element's name)
15204      */
15205     hiddenName: undefined,
15206     /**
15207      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15208      */
15209     listClass: '',
15210     /**
15211      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15212      */
15213     selectedClass: 'active',
15214     
15215     /**
15216      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15217      */
15218     shadow:'sides',
15219     /**
15220      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15221      * anchor positions (defaults to 'tl-bl')
15222      */
15223     listAlign: 'tl-bl?',
15224     /**
15225      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15226      */
15227     maxHeight: 300,
15228     /**
15229      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15230      * query specified by the allQuery config option (defaults to 'query')
15231      */
15232     triggerAction: 'query',
15233     /**
15234      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15235      * (defaults to 4, does not apply if editable = false)
15236      */
15237     minChars : 4,
15238     /**
15239      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15240      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15241      */
15242     typeAhead: false,
15243     /**
15244      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15245      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15246      */
15247     queryDelay: 500,
15248     /**
15249      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15250      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15251      */
15252     pageSize: 0,
15253     /**
15254      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15255      * when editable = true (defaults to false)
15256      */
15257     selectOnFocus:false,
15258     /**
15259      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15260      */
15261     queryParam: 'query',
15262     /**
15263      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15264      * when mode = 'remote' (defaults to 'Loading...')
15265      */
15266     loadingText: 'Loading...',
15267     /**
15268      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15269      */
15270     resizable: false,
15271     /**
15272      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15273      */
15274     handleHeight : 8,
15275     /**
15276      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15277      * traditional select (defaults to true)
15278      */
15279     editable: true,
15280     /**
15281      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15282      */
15283     allQuery: '',
15284     /**
15285      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15286      */
15287     mode: 'remote',
15288     /**
15289      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15290      * listWidth has a higher value)
15291      */
15292     minListWidth : 70,
15293     /**
15294      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15295      * allow the user to set arbitrary text into the field (defaults to false)
15296      */
15297     forceSelection:false,
15298     /**
15299      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15300      * if typeAhead = true (defaults to 250)
15301      */
15302     typeAheadDelay : 250,
15303     /**
15304      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15305      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15306      */
15307     valueNotFoundText : undefined,
15308     /**
15309      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15310      */
15311     blockFocus : false,
15312     
15313     /**
15314      * @cfg {Boolean} disableClear Disable showing of clear button.
15315      */
15316     disableClear : false,
15317     /**
15318      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15319      */
15320     alwaysQuery : false,
15321     
15322     /**
15323      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15324      */
15325     multiple : false,
15326     
15327     /**
15328      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15329      */
15330     invalidClass : "has-warning",
15331     
15332     /**
15333      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15334      */
15335     validClass : "has-success",
15336     
15337     /**
15338      * @cfg {Boolean} specialFilter (true|false) special filter default false
15339      */
15340     specialFilter : false,
15341     
15342     /**
15343      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15344      */
15345     mobileTouchView : true,
15346     
15347     /**
15348      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15349      */
15350     useNativeIOS : false,
15351     
15352     /**
15353      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15354      */
15355     mobile_restrict_height : false,
15356     
15357     ios_options : false,
15358     
15359     //private
15360     addicon : false,
15361     editicon: false,
15362     
15363     page: 0,
15364     hasQuery: false,
15365     append: false,
15366     loadNext: false,
15367     autoFocus : true,
15368     tickable : false,
15369     btnPosition : 'right',
15370     triggerList : true,
15371     showToggleBtn : true,
15372     animate : true,
15373     emptyResultText: 'Empty',
15374     triggerText : 'Select',
15375     emptyTitle : '',
15376     width : false,
15377     
15378     // element that contains real text value.. (when hidden is used..)
15379     
15380     getAutoCreate : function()
15381     {   
15382         var cfg = false;
15383         //render
15384         /*
15385          * Render classic select for iso
15386          */
15387         
15388         if(Roo.isIOS && this.useNativeIOS){
15389             cfg = this.getAutoCreateNativeIOS();
15390             return cfg;
15391         }
15392         
15393         /*
15394          * Touch Devices
15395          */
15396         
15397         if(Roo.isTouch && this.mobileTouchView){
15398             cfg = this.getAutoCreateTouchView();
15399             return cfg;;
15400         }
15401         
15402         /*
15403          *  Normal ComboBox
15404          */
15405         if(!this.tickable){
15406             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15407             return cfg;
15408         }
15409         
15410         /*
15411          *  ComboBox with tickable selections
15412          */
15413              
15414         var align = this.labelAlign || this.parentLabelAlign();
15415         
15416         cfg = {
15417             cls : 'form-group roo-combobox-tickable' //input-group
15418         };
15419         
15420         var btn_text_select = '';
15421         var btn_text_done = '';
15422         var btn_text_cancel = '';
15423         
15424         if (this.btn_text_show) {
15425             btn_text_select = 'Select';
15426             btn_text_done = 'Done';
15427             btn_text_cancel = 'Cancel'; 
15428         }
15429         
15430         var buttons = {
15431             tag : 'div',
15432             cls : 'tickable-buttons',
15433             cn : [
15434                 {
15435                     tag : 'button',
15436                     type : 'button',
15437                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15438                     //html : this.triggerText
15439                     html: btn_text_select
15440                 },
15441                 {
15442                     tag : 'button',
15443                     type : 'button',
15444                     name : 'ok',
15445                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15446                     //html : 'Done'
15447                     html: btn_text_done
15448                 },
15449                 {
15450                     tag : 'button',
15451                     type : 'button',
15452                     name : 'cancel',
15453                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15454                     //html : 'Cancel'
15455                     html: btn_text_cancel
15456                 }
15457             ]
15458         };
15459         
15460         if(this.editable){
15461             buttons.cn.unshift({
15462                 tag: 'input',
15463                 cls: 'roo-select2-search-field-input'
15464             });
15465         }
15466         
15467         var _this = this;
15468         
15469         Roo.each(buttons.cn, function(c){
15470             if (_this.size) {
15471                 c.cls += ' btn-' + _this.size;
15472             }
15473
15474             if (_this.disabled) {
15475                 c.disabled = true;
15476             }
15477         });
15478         
15479         var box = {
15480             tag: 'div',
15481             style : 'display: contents',
15482             cn: [
15483                 {
15484                     tag: 'input',
15485                     type : 'hidden',
15486                     cls: 'form-hidden-field'
15487                 },
15488                 {
15489                     tag: 'ul',
15490                     cls: 'roo-select2-choices',
15491                     cn:[
15492                         {
15493                             tag: 'li',
15494                             cls: 'roo-select2-search-field',
15495                             cn: [
15496                                 buttons
15497                             ]
15498                         }
15499                     ]
15500                 }
15501             ]
15502         };
15503         
15504         var combobox = {
15505             cls: 'roo-select2-container input-group roo-select2-container-multi',
15506             cn: [
15507                 
15508                 box
15509 //                {
15510 //                    tag: 'ul',
15511 //                    cls: 'typeahead typeahead-long dropdown-menu',
15512 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15513 //                }
15514             ]
15515         };
15516         
15517         if(this.hasFeedback && !this.allowBlank){
15518             
15519             var feedback = {
15520                 tag: 'span',
15521                 cls: 'glyphicon form-control-feedback'
15522             };
15523
15524             combobox.cn.push(feedback);
15525         }
15526         
15527         
15528         
15529         var indicator = {
15530             tag : 'i',
15531             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15532             tooltip : 'This field is required'
15533         };
15534         if (Roo.bootstrap.version == 4) {
15535             indicator = {
15536                 tag : 'i',
15537                 style : 'display:none'
15538             };
15539         }
15540         if (align ==='left' && this.fieldLabel.length) {
15541             
15542             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15543             
15544             cfg.cn = [
15545                 indicator,
15546                 {
15547                     tag: 'label',
15548                     'for' :  id,
15549                     cls : 'control-label col-form-label',
15550                     html : this.fieldLabel
15551
15552                 },
15553                 {
15554                     cls : "", 
15555                     cn: [
15556                         combobox
15557                     ]
15558                 }
15559
15560             ];
15561             
15562             var labelCfg = cfg.cn[1];
15563             var contentCfg = cfg.cn[2];
15564             
15565
15566             if(this.indicatorpos == 'right'){
15567                 
15568                 cfg.cn = [
15569                     {
15570                         tag: 'label',
15571                         'for' :  id,
15572                         cls : 'control-label col-form-label',
15573                         cn : [
15574                             {
15575                                 tag : 'span',
15576                                 html : this.fieldLabel
15577                             },
15578                             indicator
15579                         ]
15580                     },
15581                     {
15582                         cls : "",
15583                         cn: [
15584                             combobox
15585                         ]
15586                     }
15587
15588                 ];
15589                 
15590                 
15591                 
15592                 labelCfg = cfg.cn[0];
15593                 contentCfg = cfg.cn[1];
15594             
15595             }
15596             
15597             if(this.labelWidth > 12){
15598                 labelCfg.style = "width: " + this.labelWidth + 'px';
15599             }
15600             if(this.width * 1 > 0){
15601                 contentCfg.style = "width: " + this.width + 'px';
15602             }
15603             if(this.labelWidth < 13 && this.labelmd == 0){
15604                 this.labelmd = this.labelWidth;
15605             }
15606             
15607             if(this.labellg > 0){
15608                 labelCfg.cls += ' col-lg-' + this.labellg;
15609                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15610             }
15611             
15612             if(this.labelmd > 0){
15613                 labelCfg.cls += ' col-md-' + this.labelmd;
15614                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15615             }
15616             
15617             if(this.labelsm > 0){
15618                 labelCfg.cls += ' col-sm-' + this.labelsm;
15619                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15620             }
15621             
15622             if(this.labelxs > 0){
15623                 labelCfg.cls += ' col-xs-' + this.labelxs;
15624                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15625             }
15626                 
15627                 
15628         } else if ( this.fieldLabel.length) {
15629 //                Roo.log(" label");
15630                  cfg.cn = [
15631                    indicator,
15632                     {
15633                         tag: 'label',
15634                         //cls : 'input-group-addon',
15635                         html : this.fieldLabel
15636                     },
15637                     combobox
15638                 ];
15639                 
15640                 if(this.indicatorpos == 'right'){
15641                     cfg.cn = [
15642                         {
15643                             tag: 'label',
15644                             //cls : 'input-group-addon',
15645                             html : this.fieldLabel
15646                         },
15647                         indicator,
15648                         combobox
15649                     ];
15650                     
15651                 }
15652
15653         } else {
15654             
15655 //                Roo.log(" no label && no align");
15656                 cfg = combobox
15657                      
15658                 
15659         }
15660          
15661         var settings=this;
15662         ['xs','sm','md','lg'].map(function(size){
15663             if (settings[size]) {
15664                 cfg.cls += ' col-' + size + '-' + settings[size];
15665             }
15666         });
15667         
15668         return cfg;
15669         
15670     },
15671     
15672     _initEventsCalled : false,
15673     
15674     // private
15675     initEvents: function()
15676     {   
15677         if (this._initEventsCalled) { // as we call render... prevent looping...
15678             return;
15679         }
15680         this._initEventsCalled = true;
15681         
15682         if (!this.store) {
15683             throw "can not find store for combo";
15684         }
15685         
15686         this.indicator = this.indicatorEl();
15687         
15688         this.store = Roo.factory(this.store, Roo.data);
15689         this.store.parent = this;
15690         
15691         // if we are building from html. then this element is so complex, that we can not really
15692         // use the rendered HTML.
15693         // so we have to trash and replace the previous code.
15694         if (Roo.XComponent.build_from_html) {
15695             // remove this element....
15696             var e = this.el.dom, k=0;
15697             while (e ) { e = e.previousSibling;  ++k;}
15698
15699             this.el.remove();
15700             
15701             this.el=false;
15702             this.rendered = false;
15703             
15704             this.render(this.parent().getChildContainer(true), k);
15705         }
15706         
15707         if(Roo.isIOS && this.useNativeIOS){
15708             this.initIOSView();
15709             return;
15710         }
15711         
15712         /*
15713          * Touch Devices
15714          */
15715         
15716         if(Roo.isTouch && this.mobileTouchView){
15717             this.initTouchView();
15718             return;
15719         }
15720         
15721         if(this.tickable){
15722             this.initTickableEvents();
15723             return;
15724         }
15725         
15726         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15727         
15728         if(this.hiddenName){
15729             
15730             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15731             
15732             this.hiddenField.dom.value =
15733                 this.hiddenValue !== undefined ? this.hiddenValue :
15734                 this.value !== undefined ? this.value : '';
15735
15736             // prevent input submission
15737             this.el.dom.removeAttribute('name');
15738             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15739              
15740              
15741         }
15742         //if(Roo.isGecko){
15743         //    this.el.dom.setAttribute('autocomplete', 'off');
15744         //}
15745         
15746         var cls = 'x-combo-list';
15747         
15748         //this.list = new Roo.Layer({
15749         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15750         //});
15751         
15752         var _this = this;
15753         
15754         (function(){
15755             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15756             _this.list.setWidth(lw);
15757         }).defer(100);
15758         
15759         this.list.on('mouseover', this.onViewOver, this);
15760         this.list.on('mousemove', this.onViewMove, this);
15761         this.list.on('scroll', this.onViewScroll, this);
15762         
15763         /*
15764         this.list.swallowEvent('mousewheel');
15765         this.assetHeight = 0;
15766
15767         if(this.title){
15768             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15769             this.assetHeight += this.header.getHeight();
15770         }
15771
15772         this.innerList = this.list.createChild({cls:cls+'-inner'});
15773         this.innerList.on('mouseover', this.onViewOver, this);
15774         this.innerList.on('mousemove', this.onViewMove, this);
15775         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15776         
15777         if(this.allowBlank && !this.pageSize && !this.disableClear){
15778             this.footer = this.list.createChild({cls:cls+'-ft'});
15779             this.pageTb = new Roo.Toolbar(this.footer);
15780            
15781         }
15782         if(this.pageSize){
15783             this.footer = this.list.createChild({cls:cls+'-ft'});
15784             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15785                     {pageSize: this.pageSize});
15786             
15787         }
15788         
15789         if (this.pageTb && this.allowBlank && !this.disableClear) {
15790             var _this = this;
15791             this.pageTb.add(new Roo.Toolbar.Fill(), {
15792                 cls: 'x-btn-icon x-btn-clear',
15793                 text: '&#160;',
15794                 handler: function()
15795                 {
15796                     _this.collapse();
15797                     _this.clearValue();
15798                     _this.onSelect(false, -1);
15799                 }
15800             });
15801         }
15802         if (this.footer) {
15803             this.assetHeight += this.footer.getHeight();
15804         }
15805         */
15806             
15807         if(!this.tpl){
15808             this.tpl = Roo.bootstrap.version == 4 ?
15809                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15810                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15811         }
15812
15813         this.view = new Roo.View(this.list, this.tpl, {
15814             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15815         });
15816         //this.view.wrapEl.setDisplayed(false);
15817         this.view.on('click', this.onViewClick, this);
15818         
15819         
15820         this.store.on('beforeload', this.onBeforeLoad, this);
15821         this.store.on('load', this.onLoad, this);
15822         this.store.on('loadexception', this.onLoadException, this);
15823         /*
15824         if(this.resizable){
15825             this.resizer = new Roo.Resizable(this.list,  {
15826                pinned:true, handles:'se'
15827             });
15828             this.resizer.on('resize', function(r, w, h){
15829                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15830                 this.listWidth = w;
15831                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15832                 this.restrictHeight();
15833             }, this);
15834             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15835         }
15836         */
15837         if(!this.editable){
15838             this.editable = true;
15839             this.setEditable(false);
15840         }
15841         
15842         /*
15843         
15844         if (typeof(this.events.add.listeners) != 'undefined') {
15845             
15846             this.addicon = this.wrap.createChild(
15847                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15848        
15849             this.addicon.on('click', function(e) {
15850                 this.fireEvent('add', this);
15851             }, this);
15852         }
15853         if (typeof(this.events.edit.listeners) != 'undefined') {
15854             
15855             this.editicon = this.wrap.createChild(
15856                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15857             if (this.addicon) {
15858                 this.editicon.setStyle('margin-left', '40px');
15859             }
15860             this.editicon.on('click', function(e) {
15861                 
15862                 // we fire even  if inothing is selected..
15863                 this.fireEvent('edit', this, this.lastData );
15864                 
15865             }, this);
15866         }
15867         */
15868         
15869         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15870             "up" : function(e){
15871                 this.inKeyMode = true;
15872                 this.selectPrev();
15873             },
15874
15875             "down" : function(e){
15876                 if(!this.isExpanded()){
15877                     this.onTriggerClick();
15878                 }else{
15879                     this.inKeyMode = true;
15880                     this.selectNext();
15881                 }
15882             },
15883
15884             "enter" : function(e){
15885 //                this.onViewClick();
15886                 //return true;
15887                 this.collapse();
15888                 
15889                 if(this.fireEvent("specialkey", this, e)){
15890                     this.onViewClick(false);
15891                 }
15892                 
15893                 return true;
15894             },
15895
15896             "esc" : function(e){
15897                 this.collapse();
15898             },
15899
15900             "tab" : function(e){
15901                 this.collapse();
15902                 
15903                 if(this.fireEvent("specialkey", this, e)){
15904                     this.onViewClick(false);
15905                 }
15906                 
15907                 return true;
15908             },
15909
15910             scope : this,
15911
15912             doRelay : function(foo, bar, hname){
15913                 if(hname == 'down' || this.scope.isExpanded()){
15914                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15915                 }
15916                 return true;
15917             },
15918
15919             forceKeyDown: true
15920         });
15921         
15922         
15923         this.queryDelay = Math.max(this.queryDelay || 10,
15924                 this.mode == 'local' ? 10 : 250);
15925         
15926         
15927         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15928         
15929         if(this.typeAhead){
15930             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15931         }
15932         if(this.editable !== false){
15933             this.inputEl().on("keyup", this.onKeyUp, this);
15934         }
15935         if(this.forceSelection){
15936             this.inputEl().on('blur', this.doForce, this);
15937         }
15938         
15939         if(this.multiple){
15940             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15941             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15942         }
15943     },
15944     
15945     initTickableEvents: function()
15946     {   
15947         this.createList();
15948         
15949         if(this.hiddenName){
15950             
15951             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15952             
15953             this.hiddenField.dom.value =
15954                 this.hiddenValue !== undefined ? this.hiddenValue :
15955                 this.value !== undefined ? this.value : '';
15956
15957             // prevent input submission
15958             this.el.dom.removeAttribute('name');
15959             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15960              
15961              
15962         }
15963         
15964 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15965         
15966         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15967         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15968         if(this.triggerList){
15969             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15970         }
15971          
15972         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15973         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15974         
15975         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15976         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15977         
15978         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15979         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15980         
15981         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15982         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15983         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15984         
15985         this.okBtn.hide();
15986         this.cancelBtn.hide();
15987         
15988         var _this = this;
15989         
15990         (function(){
15991             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15992             _this.list.setWidth(lw);
15993         }).defer(100);
15994         
15995         this.list.on('mouseover', this.onViewOver, this);
15996         this.list.on('mousemove', this.onViewMove, this);
15997         
15998         this.list.on('scroll', this.onViewScroll, this);
15999         
16000         if(!this.tpl){
16001             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
16002                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16003         }
16004
16005         this.view = new Roo.View(this.list, this.tpl, {
16006             singleSelect:true,
16007             tickable:true,
16008             parent:this,
16009             store: this.store,
16010             selectedClass: this.selectedClass
16011         });
16012         
16013         //this.view.wrapEl.setDisplayed(false);
16014         this.view.on('click', this.onViewClick, this);
16015         
16016         
16017         
16018         this.store.on('beforeload', this.onBeforeLoad, this);
16019         this.store.on('load', this.onLoad, this);
16020         this.store.on('loadexception', this.onLoadException, this);
16021         
16022         if(this.editable){
16023             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16024                 "up" : function(e){
16025                     this.inKeyMode = true;
16026                     this.selectPrev();
16027                 },
16028
16029                 "down" : function(e){
16030                     this.inKeyMode = true;
16031                     this.selectNext();
16032                 },
16033
16034                 "enter" : function(e){
16035                     if(this.fireEvent("specialkey", this, e)){
16036                         this.onViewClick(false);
16037                     }
16038                     
16039                     return true;
16040                 },
16041
16042                 "esc" : function(e){
16043                     this.onTickableFooterButtonClick(e, false, false);
16044                 },
16045
16046                 "tab" : function(e){
16047                     this.fireEvent("specialkey", this, e);
16048                     
16049                     this.onTickableFooterButtonClick(e, false, false);
16050                     
16051                     return true;
16052                 },
16053
16054                 scope : this,
16055
16056                 doRelay : function(e, fn, key){
16057                     if(this.scope.isExpanded()){
16058                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16059                     }
16060                     return true;
16061                 },
16062
16063                 forceKeyDown: true
16064             });
16065         }
16066         
16067         this.queryDelay = Math.max(this.queryDelay || 10,
16068                 this.mode == 'local' ? 10 : 250);
16069         
16070         
16071         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16072         
16073         if(this.typeAhead){
16074             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16075         }
16076         
16077         if(this.editable !== false){
16078             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16079         }
16080         
16081         this.indicator = this.indicatorEl();
16082         
16083         if(this.indicator){
16084             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16085             this.indicator.hide();
16086         }
16087         
16088     },
16089
16090     onDestroy : function(){
16091         if(this.view){
16092             this.view.setStore(null);
16093             this.view.el.removeAllListeners();
16094             this.view.el.remove();
16095             this.view.purgeListeners();
16096         }
16097         if(this.list){
16098             this.list.dom.innerHTML  = '';
16099         }
16100         
16101         if(this.store){
16102             this.store.un('beforeload', this.onBeforeLoad, this);
16103             this.store.un('load', this.onLoad, this);
16104             this.store.un('loadexception', this.onLoadException, this);
16105         }
16106         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16107     },
16108
16109     // private
16110     fireKey : function(e){
16111         if(e.isNavKeyPress() && !this.list.isVisible()){
16112             this.fireEvent("specialkey", this, e);
16113         }
16114     },
16115
16116     // private
16117     onResize: function(w, h)
16118     {
16119         
16120         
16121 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16122 //        
16123 //        if(typeof w != 'number'){
16124 //            // we do not handle it!?!?
16125 //            return;
16126 //        }
16127 //        var tw = this.trigger.getWidth();
16128 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16129 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16130 //        var x = w - tw;
16131 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16132 //            
16133 //        //this.trigger.setStyle('left', x+'px');
16134 //        
16135 //        if(this.list && this.listWidth === undefined){
16136 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16137 //            this.list.setWidth(lw);
16138 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16139 //        }
16140         
16141     
16142         
16143     },
16144
16145     /**
16146      * Allow or prevent the user from directly editing the field text.  If false is passed,
16147      * the user will only be able to select from the items defined in the dropdown list.  This method
16148      * is the runtime equivalent of setting the 'editable' config option at config time.
16149      * @param {Boolean} value True to allow the user to directly edit the field text
16150      */
16151     setEditable : function(value){
16152         if(value == this.editable){
16153             return;
16154         }
16155         this.editable = value;
16156         if(!value){
16157             this.inputEl().dom.setAttribute('readOnly', true);
16158             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16159             this.inputEl().addClass('x-combo-noedit');
16160         }else{
16161             this.inputEl().dom.setAttribute('readOnly', false);
16162             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16163             this.inputEl().removeClass('x-combo-noedit');
16164         }
16165     },
16166
16167     // private
16168     
16169     onBeforeLoad : function(combo,opts){
16170         if(!this.hasFocus){
16171             return;
16172         }
16173          if (!opts.add) {
16174             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16175          }
16176         this.restrictHeight();
16177         this.selectedIndex = -1;
16178     },
16179
16180     // private
16181     onLoad : function(){
16182         
16183         this.hasQuery = false;
16184         
16185         if(!this.hasFocus){
16186             return;
16187         }
16188         
16189         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16190             this.loading.hide();
16191         }
16192         
16193         if(this.store.getCount() > 0){
16194             
16195             this.expand();
16196             this.restrictHeight();
16197             if(this.lastQuery == this.allQuery){
16198                 if(this.editable && !this.tickable){
16199                     this.inputEl().dom.select();
16200                 }
16201                 
16202                 if(
16203                     !this.selectByValue(this.value, true) &&
16204                     this.autoFocus && 
16205                     (
16206                         !this.store.lastOptions ||
16207                         typeof(this.store.lastOptions.add) == 'undefined' || 
16208                         this.store.lastOptions.add != true
16209                     )
16210                 ){
16211                     this.select(0, true);
16212                 }
16213             }else{
16214                 if(this.autoFocus){
16215                     this.selectNext();
16216                 }
16217                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16218                     this.taTask.delay(this.typeAheadDelay);
16219                 }
16220             }
16221         }else{
16222             this.onEmptyResults();
16223         }
16224         
16225         //this.el.focus();
16226     },
16227     // private
16228     onLoadException : function()
16229     {
16230         this.hasQuery = false;
16231         
16232         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16233             this.loading.hide();
16234         }
16235         
16236         if(this.tickable && this.editable){
16237             return;
16238         }
16239         
16240         this.collapse();
16241         // only causes errors at present
16242         //Roo.log(this.store.reader.jsonData);
16243         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16244             // fixme
16245             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16246         //}
16247         
16248         
16249     },
16250     // private
16251     onTypeAhead : function(){
16252         if(this.store.getCount() > 0){
16253             var r = this.store.getAt(0);
16254             var newValue = r.data[this.displayField];
16255             var len = newValue.length;
16256             var selStart = this.getRawValue().length;
16257             
16258             if(selStart != len){
16259                 this.setRawValue(newValue);
16260                 this.selectText(selStart, newValue.length);
16261             }
16262         }
16263     },
16264
16265     // private
16266     onSelect : function(record, index){
16267         
16268         if(this.fireEvent('beforeselect', this, record, index) !== false){
16269         
16270             this.setFromData(index > -1 ? record.data : false);
16271             
16272             this.collapse();
16273             this.fireEvent('select', this, record, index);
16274         }
16275     },
16276
16277     /**
16278      * Returns the currently selected field value or empty string if no value is set.
16279      * @return {String} value The selected value
16280      */
16281     getValue : function()
16282     {
16283         if(Roo.isIOS && this.useNativeIOS){
16284             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16285         }
16286         
16287         if(this.multiple){
16288             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16289         }
16290         
16291         if(this.valueField){
16292             return typeof this.value != 'undefined' ? this.value : '';
16293         }else{
16294             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16295         }
16296     },
16297     
16298     getRawValue : function()
16299     {
16300         if(Roo.isIOS && this.useNativeIOS){
16301             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16302         }
16303         
16304         var v = this.inputEl().getValue();
16305         
16306         return v;
16307     },
16308
16309     /**
16310      * Clears any text/value currently set in the field
16311      */
16312     clearValue : function(){
16313         
16314         if(this.hiddenField){
16315             this.hiddenField.dom.value = '';
16316         }
16317         this.value = '';
16318         this.setRawValue('');
16319         this.lastSelectionText = '';
16320         this.lastData = false;
16321         
16322         var close = this.closeTriggerEl();
16323         
16324         if(close){
16325             close.hide();
16326         }
16327         
16328         this.validate();
16329         
16330     },
16331
16332     /**
16333      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16334      * will be displayed in the field.  If the value does not match the data value of an existing item,
16335      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16336      * Otherwise the field will be blank (although the value will still be set).
16337      * @param {String} value The value to match
16338      */
16339     setValue : function(v)
16340     {
16341         if(Roo.isIOS && this.useNativeIOS){
16342             this.setIOSValue(v);
16343             return;
16344         }
16345         
16346         if(this.multiple){
16347             this.syncValue();
16348             return;
16349         }
16350         
16351         var text = v;
16352         if(this.valueField){
16353             var r = this.findRecord(this.valueField, v);
16354             if(r){
16355                 text = r.data[this.displayField];
16356             }else if(this.valueNotFoundText !== undefined){
16357                 text = this.valueNotFoundText;
16358             }
16359         }
16360         this.lastSelectionText = text;
16361         if(this.hiddenField){
16362             this.hiddenField.dom.value = v;
16363         }
16364         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16365         this.value = v;
16366         
16367         var close = this.closeTriggerEl();
16368         
16369         if(close){
16370             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16371         }
16372         
16373         this.validate();
16374     },
16375     /**
16376      * @property {Object} the last set data for the element
16377      */
16378     
16379     lastData : false,
16380     /**
16381      * Sets the value of the field based on a object which is related to the record format for the store.
16382      * @param {Object} value the value to set as. or false on reset?
16383      */
16384     setFromData : function(o){
16385         
16386         if(this.multiple){
16387             this.addItem(o);
16388             return;
16389         }
16390             
16391         var dv = ''; // display value
16392         var vv = ''; // value value..
16393         this.lastData = o;
16394         if (this.displayField) {
16395             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16396         } else {
16397             // this is an error condition!!!
16398             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16399         }
16400         
16401         if(this.valueField){
16402             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16403         }
16404         
16405         var close = this.closeTriggerEl();
16406         
16407         if(close){
16408             if(dv.length || vv * 1 > 0){
16409                 close.show() ;
16410                 this.blockFocus=true;
16411             } else {
16412                 close.hide();
16413             }             
16414         }
16415         
16416         if(this.hiddenField){
16417             this.hiddenField.dom.value = vv;
16418             
16419             this.lastSelectionText = dv;
16420             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16421             this.value = vv;
16422             return;
16423         }
16424         // no hidden field.. - we store the value in 'value', but still display
16425         // display field!!!!
16426         this.lastSelectionText = dv;
16427         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16428         this.value = vv;
16429         
16430         
16431         
16432     },
16433     // private
16434     reset : function(){
16435         // overridden so that last data is reset..
16436         
16437         if(this.multiple){
16438             this.clearItem();
16439             return;
16440         }
16441         
16442         this.setValue(this.originalValue);
16443         //this.clearInvalid();
16444         this.lastData = false;
16445         if (this.view) {
16446             this.view.clearSelections();
16447         }
16448         
16449         this.validate();
16450     },
16451     // private
16452     findRecord : function(prop, value){
16453         var record;
16454         if(this.store.getCount() > 0){
16455             this.store.each(function(r){
16456                 if(r.data[prop] == value){
16457                     record = r;
16458                     return false;
16459                 }
16460                 return true;
16461             });
16462         }
16463         return record;
16464     },
16465     
16466     getName: function()
16467     {
16468         // returns hidden if it's set..
16469         if (!this.rendered) {return ''};
16470         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16471         
16472     },
16473     // private
16474     onViewMove : function(e, t){
16475         this.inKeyMode = false;
16476     },
16477
16478     // private
16479     onViewOver : function(e, t){
16480         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16481             return;
16482         }
16483         var item = this.view.findItemFromChild(t);
16484         
16485         if(item){
16486             var index = this.view.indexOf(item);
16487             this.select(index, false);
16488         }
16489     },
16490
16491     // private
16492     onViewClick : function(view, doFocus, el, e)
16493     {
16494         var index = this.view.getSelectedIndexes()[0];
16495         
16496         var r = this.store.getAt(index);
16497         
16498         if(this.tickable){
16499             
16500             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16501                 return;
16502             }
16503             
16504             var rm = false;
16505             var _this = this;
16506             
16507             Roo.each(this.tickItems, function(v,k){
16508                 
16509                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16510                     Roo.log(v);
16511                     _this.tickItems.splice(k, 1);
16512                     
16513                     if(typeof(e) == 'undefined' && view == false){
16514                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16515                     }
16516                     
16517                     rm = true;
16518                     return;
16519                 }
16520             });
16521             
16522             if(rm){
16523                 return;
16524             }
16525             
16526             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16527                 this.tickItems.push(r.data);
16528             }
16529             
16530             if(typeof(e) == 'undefined' && view == false){
16531                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16532             }
16533                     
16534             return;
16535         }
16536         
16537         if(r){
16538             this.onSelect(r, index);
16539         }
16540         if(doFocus !== false && !this.blockFocus){
16541             this.inputEl().focus();
16542         }
16543     },
16544
16545     // private
16546     restrictHeight : function(){
16547         //this.innerList.dom.style.height = '';
16548         //var inner = this.innerList.dom;
16549         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16550         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16551         //this.list.beginUpdate();
16552         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16553         this.list.alignTo(this.inputEl(), this.listAlign);
16554         this.list.alignTo(this.inputEl(), this.listAlign);
16555         //this.list.endUpdate();
16556     },
16557
16558     // private
16559     onEmptyResults : function(){
16560         
16561         if(this.tickable && this.editable){
16562             this.hasFocus = false;
16563             this.restrictHeight();
16564             return;
16565         }
16566         
16567         this.collapse();
16568     },
16569
16570     /**
16571      * Returns true if the dropdown list is expanded, else false.
16572      */
16573     isExpanded : function(){
16574         return this.list.isVisible();
16575     },
16576
16577     /**
16578      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16579      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16580      * @param {String} value The data value of the item to select
16581      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16582      * selected item if it is not currently in view (defaults to true)
16583      * @return {Boolean} True if the value matched an item in the list, else false
16584      */
16585     selectByValue : function(v, scrollIntoView){
16586         if(v !== undefined && v !== null){
16587             var r = this.findRecord(this.valueField || this.displayField, v);
16588             if(r){
16589                 this.select(this.store.indexOf(r), scrollIntoView);
16590                 return true;
16591             }
16592         }
16593         return false;
16594     },
16595
16596     /**
16597      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16598      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16599      * @param {Number} index The zero-based index of the list item to select
16600      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16601      * selected item if it is not currently in view (defaults to true)
16602      */
16603     select : function(index, scrollIntoView){
16604         this.selectedIndex = index;
16605         this.view.select(index);
16606         if(scrollIntoView !== false){
16607             var el = this.view.getNode(index);
16608             /*
16609              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16610              */
16611             if(el){
16612                 this.list.scrollChildIntoView(el, false);
16613             }
16614         }
16615     },
16616
16617     // private
16618     selectNext : function(){
16619         var ct = this.store.getCount();
16620         if(ct > 0){
16621             if(this.selectedIndex == -1){
16622                 this.select(0);
16623             }else if(this.selectedIndex < ct-1){
16624                 this.select(this.selectedIndex+1);
16625             }
16626         }
16627     },
16628
16629     // private
16630     selectPrev : function(){
16631         var ct = this.store.getCount();
16632         if(ct > 0){
16633             if(this.selectedIndex == -1){
16634                 this.select(0);
16635             }else if(this.selectedIndex != 0){
16636                 this.select(this.selectedIndex-1);
16637             }
16638         }
16639     },
16640
16641     // private
16642     onKeyUp : function(e){
16643         if(this.editable !== false && !e.isSpecialKey()){
16644             this.lastKey = e.getKey();
16645             this.dqTask.delay(this.queryDelay);
16646         }
16647     },
16648
16649     // private
16650     validateBlur : function(){
16651         return !this.list || !this.list.isVisible();   
16652     },
16653
16654     // private
16655     initQuery : function(){
16656         
16657         var v = this.getRawValue();
16658         
16659         if(this.tickable && this.editable){
16660             v = this.tickableInputEl().getValue();
16661         }
16662         
16663         this.doQuery(v);
16664     },
16665
16666     // private
16667     doForce : function(){
16668         if(this.inputEl().dom.value.length > 0){
16669             this.inputEl().dom.value =
16670                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16671              
16672         }
16673     },
16674
16675     /**
16676      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16677      * query allowing the query action to be canceled if needed.
16678      * @param {String} query The SQL query to execute
16679      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16680      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16681      * saved in the current store (defaults to false)
16682      */
16683     doQuery : function(q, forceAll){
16684         
16685         if(q === undefined || q === null){
16686             q = '';
16687         }
16688         var qe = {
16689             query: q,
16690             forceAll: forceAll,
16691             combo: this,
16692             cancel:false
16693         };
16694         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16695             return false;
16696         }
16697         q = qe.query;
16698         
16699         forceAll = qe.forceAll;
16700         if(forceAll === true || (q.length >= this.minChars)){
16701             
16702             this.hasQuery = true;
16703             
16704             if(this.lastQuery != q || this.alwaysQuery){
16705                 this.lastQuery = q;
16706                 if(this.mode == 'local'){
16707                     this.selectedIndex = -1;
16708                     if(forceAll){
16709                         this.store.clearFilter();
16710                     }else{
16711                         
16712                         if(this.specialFilter){
16713                             this.fireEvent('specialfilter', this);
16714                             this.onLoad();
16715                             return;
16716                         }
16717                         
16718                         this.store.filter(this.displayField, q);
16719                     }
16720                     
16721                     this.store.fireEvent("datachanged", this.store);
16722                     
16723                     this.onLoad();
16724                     
16725                     
16726                 }else{
16727                     
16728                     this.store.baseParams[this.queryParam] = q;
16729                     
16730                     var options = {params : this.getParams(q)};
16731                     
16732                     if(this.loadNext){
16733                         options.add = true;
16734                         options.params.start = this.page * this.pageSize;
16735                     }
16736                     
16737                     this.store.load(options);
16738                     
16739                     /*
16740                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16741                      *  we should expand the list on onLoad
16742                      *  so command out it
16743                      */
16744 //                    this.expand();
16745                 }
16746             }else{
16747                 this.selectedIndex = -1;
16748                 this.onLoad();   
16749             }
16750         }
16751         
16752         this.loadNext = false;
16753     },
16754     
16755     // private
16756     getParams : function(q){
16757         var p = {};
16758         //p[this.queryParam] = q;
16759         
16760         if(this.pageSize){
16761             p.start = 0;
16762             p.limit = this.pageSize;
16763         }
16764         return p;
16765     },
16766
16767     /**
16768      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16769      */
16770     collapse : function(){
16771         if(!this.isExpanded()){
16772             return;
16773         }
16774         
16775         this.list.hide();
16776         
16777         this.hasFocus = false;
16778         
16779         if(this.tickable){
16780             this.okBtn.hide();
16781             this.cancelBtn.hide();
16782             this.trigger.show();
16783             
16784             if(this.editable){
16785                 this.tickableInputEl().dom.value = '';
16786                 this.tickableInputEl().blur();
16787             }
16788             
16789         }
16790         
16791         Roo.get(document).un('mousedown', this.collapseIf, this);
16792         Roo.get(document).un('mousewheel', this.collapseIf, this);
16793         if (!this.editable) {
16794             Roo.get(document).un('keydown', this.listKeyPress, this);
16795         }
16796         this.fireEvent('collapse', this);
16797         
16798         this.validate();
16799     },
16800
16801     // private
16802     collapseIf : function(e){
16803         var in_combo  = e.within(this.el);
16804         var in_list =  e.within(this.list);
16805         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16806         
16807         if (in_combo || in_list || is_list) {
16808             //e.stopPropagation();
16809             return;
16810         }
16811         
16812         if(this.tickable){
16813             this.onTickableFooterButtonClick(e, false, false);
16814         }
16815
16816         this.collapse();
16817         
16818     },
16819
16820     /**
16821      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16822      */
16823     expand : function(){
16824        
16825         if(this.isExpanded() || !this.hasFocus){
16826             return;
16827         }
16828         
16829         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16830         this.list.setWidth(lw);
16831         
16832         Roo.log('expand');
16833         
16834         this.list.show();
16835         
16836         this.restrictHeight();
16837         
16838         if(this.tickable){
16839             
16840             this.tickItems = Roo.apply([], this.item);
16841             
16842             this.okBtn.show();
16843             this.cancelBtn.show();
16844             this.trigger.hide();
16845             
16846             if(this.editable){
16847                 this.tickableInputEl().focus();
16848             }
16849             
16850         }
16851         
16852         Roo.get(document).on('mousedown', this.collapseIf, this);
16853         Roo.get(document).on('mousewheel', this.collapseIf, this);
16854         if (!this.editable) {
16855             Roo.get(document).on('keydown', this.listKeyPress, this);
16856         }
16857         
16858         this.fireEvent('expand', this);
16859     },
16860
16861     // private
16862     // Implements the default empty TriggerField.onTriggerClick function
16863     onTriggerClick : function(e)
16864     {
16865         Roo.log('trigger click');
16866         
16867         if(this.disabled || !this.triggerList){
16868             return;
16869         }
16870         
16871         this.page = 0;
16872         this.loadNext = false;
16873         
16874         if(this.isExpanded()){
16875             this.collapse();
16876             if (!this.blockFocus) {
16877                 this.inputEl().focus();
16878             }
16879             
16880         }else {
16881             this.hasFocus = true;
16882             if(this.triggerAction == 'all') {
16883                 this.doQuery(this.allQuery, true);
16884             } else {
16885                 this.doQuery(this.getRawValue());
16886             }
16887             if (!this.blockFocus) {
16888                 this.inputEl().focus();
16889             }
16890         }
16891     },
16892     
16893     onTickableTriggerClick : function(e)
16894     {
16895         if(this.disabled){
16896             return;
16897         }
16898         
16899         this.page = 0;
16900         this.loadNext = false;
16901         this.hasFocus = true;
16902         
16903         if(this.triggerAction == 'all') {
16904             this.doQuery(this.allQuery, true);
16905         } else {
16906             this.doQuery(this.getRawValue());
16907         }
16908     },
16909     
16910     onSearchFieldClick : function(e)
16911     {
16912         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16913             this.onTickableFooterButtonClick(e, false, false);
16914             return;
16915         }
16916         
16917         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16918             return;
16919         }
16920         
16921         this.page = 0;
16922         this.loadNext = false;
16923         this.hasFocus = true;
16924         
16925         if(this.triggerAction == 'all') {
16926             this.doQuery(this.allQuery, true);
16927         } else {
16928             this.doQuery(this.getRawValue());
16929         }
16930     },
16931     
16932     listKeyPress : function(e)
16933     {
16934         //Roo.log('listkeypress');
16935         // scroll to first matching element based on key pres..
16936         if (e.isSpecialKey()) {
16937             return false;
16938         }
16939         var k = String.fromCharCode(e.getKey()).toUpperCase();
16940         //Roo.log(k);
16941         var match  = false;
16942         var csel = this.view.getSelectedNodes();
16943         var cselitem = false;
16944         if (csel.length) {
16945             var ix = this.view.indexOf(csel[0]);
16946             cselitem  = this.store.getAt(ix);
16947             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16948                 cselitem = false;
16949             }
16950             
16951         }
16952         
16953         this.store.each(function(v) { 
16954             if (cselitem) {
16955                 // start at existing selection.
16956                 if (cselitem.id == v.id) {
16957                     cselitem = false;
16958                 }
16959                 return true;
16960             }
16961                 
16962             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16963                 match = this.store.indexOf(v);
16964                 return false;
16965             }
16966             return true;
16967         }, this);
16968         
16969         if (match === false) {
16970             return true; // no more action?
16971         }
16972         // scroll to?
16973         this.view.select(match);
16974         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16975         sn.scrollIntoView(sn.dom.parentNode, false);
16976     },
16977     
16978     onViewScroll : function(e, t){
16979         
16980         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){
16981             return;
16982         }
16983         
16984         this.hasQuery = true;
16985         
16986         this.loading = this.list.select('.loading', true).first();
16987         
16988         if(this.loading === null){
16989             this.list.createChild({
16990                 tag: 'div',
16991                 cls: 'loading roo-select2-more-results roo-select2-active',
16992                 html: 'Loading more results...'
16993             });
16994             
16995             this.loading = this.list.select('.loading', true).first();
16996             
16997             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16998             
16999             this.loading.hide();
17000         }
17001         
17002         this.loading.show();
17003         
17004         var _combo = this;
17005         
17006         this.page++;
17007         this.loadNext = true;
17008         
17009         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17010         
17011         return;
17012     },
17013     
17014     addItem : function(o)
17015     {   
17016         var dv = ''; // display value
17017         
17018         if (this.displayField) {
17019             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17020         } else {
17021             // this is an error condition!!!
17022             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17023         }
17024         
17025         if(!dv.length){
17026             return;
17027         }
17028         
17029         var choice = this.choices.createChild({
17030             tag: 'li',
17031             cls: 'roo-select2-search-choice',
17032             cn: [
17033                 {
17034                     tag: 'div',
17035                     html: dv
17036                 },
17037                 {
17038                     tag: 'a',
17039                     href: '#',
17040                     cls: 'roo-select2-search-choice-close fa fa-times',
17041                     tabindex: '-1'
17042                 }
17043             ]
17044             
17045         }, this.searchField);
17046         
17047         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17048         
17049         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17050         
17051         this.item.push(o);
17052         
17053         this.lastData = o;
17054         
17055         this.syncValue();
17056         
17057         this.inputEl().dom.value = '';
17058         
17059         this.validate();
17060     },
17061     
17062     onRemoveItem : function(e, _self, o)
17063     {
17064         e.preventDefault();
17065         
17066         this.lastItem = Roo.apply([], this.item);
17067         
17068         var index = this.item.indexOf(o.data) * 1;
17069         
17070         if( index < 0){
17071             Roo.log('not this item?!');
17072             return;
17073         }
17074         
17075         this.item.splice(index, 1);
17076         o.item.remove();
17077         
17078         this.syncValue();
17079         
17080         this.fireEvent('remove', this, e);
17081         
17082         this.validate();
17083         
17084     },
17085     
17086     syncValue : function()
17087     {
17088         if(!this.item.length){
17089             this.clearValue();
17090             return;
17091         }
17092             
17093         var value = [];
17094         var _this = this;
17095         Roo.each(this.item, function(i){
17096             if(_this.valueField){
17097                 value.push(i[_this.valueField]);
17098                 return;
17099             }
17100
17101             value.push(i);
17102         });
17103
17104         this.value = value.join(',');
17105
17106         if(this.hiddenField){
17107             this.hiddenField.dom.value = this.value;
17108         }
17109         
17110         this.store.fireEvent("datachanged", this.store);
17111         
17112         this.validate();
17113     },
17114     
17115     clearItem : function()
17116     {
17117         if(!this.multiple){
17118             return;
17119         }
17120         
17121         this.item = [];
17122         
17123         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17124            c.remove();
17125         });
17126         
17127         this.syncValue();
17128         
17129         this.validate();
17130         
17131         if(this.tickable && !Roo.isTouch){
17132             this.view.refresh();
17133         }
17134     },
17135     
17136     inputEl: function ()
17137     {
17138         if(Roo.isIOS && this.useNativeIOS){
17139             return this.el.select('select.roo-ios-select', true).first();
17140         }
17141         
17142         if(Roo.isTouch && this.mobileTouchView){
17143             return this.el.select('input.form-control',true).first();
17144         }
17145         
17146         if(this.tickable){
17147             return this.searchField;
17148         }
17149         
17150         return this.el.select('input.form-control',true).first();
17151     },
17152     
17153     onTickableFooterButtonClick : function(e, btn, el)
17154     {
17155         e.preventDefault();
17156         
17157         this.lastItem = Roo.apply([], this.item);
17158         
17159         if(btn && btn.name == 'cancel'){
17160             this.tickItems = Roo.apply([], this.item);
17161             this.collapse();
17162             return;
17163         }
17164         
17165         this.clearItem();
17166         
17167         var _this = this;
17168         
17169         Roo.each(this.tickItems, function(o){
17170             _this.addItem(o);
17171         });
17172         
17173         this.collapse();
17174         
17175     },
17176     
17177     validate : function()
17178     {
17179         if(this.getVisibilityEl().hasClass('hidden')){
17180             return true;
17181         }
17182         
17183         var v = this.getRawValue();
17184         
17185         if(this.multiple){
17186             v = this.getValue();
17187         }
17188         
17189         if(this.disabled || this.allowBlank || v.length){
17190             this.markValid();
17191             return true;
17192         }
17193         
17194         this.markInvalid();
17195         return false;
17196     },
17197     
17198     tickableInputEl : function()
17199     {
17200         if(!this.tickable || !this.editable){
17201             return this.inputEl();
17202         }
17203         
17204         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17205     },
17206     
17207     
17208     getAutoCreateTouchView : function()
17209     {
17210         var id = Roo.id();
17211         
17212         var cfg = {
17213             cls: 'form-group' //input-group
17214         };
17215         
17216         var input =  {
17217             tag: 'input',
17218             id : id,
17219             type : this.inputType,
17220             cls : 'form-control x-combo-noedit',
17221             autocomplete: 'new-password',
17222             placeholder : this.placeholder || '',
17223             readonly : true
17224         };
17225         
17226         if (this.name) {
17227             input.name = this.name;
17228         }
17229         
17230         if (this.size) {
17231             input.cls += ' input-' + this.size;
17232         }
17233         
17234         if (this.disabled) {
17235             input.disabled = true;
17236         }
17237         
17238         var inputblock = {
17239             cls : 'roo-combobox-wrap',
17240             cn : [
17241                 input
17242             ]
17243         };
17244         
17245         if(this.before){
17246             inputblock.cls += ' input-group';
17247             
17248             inputblock.cn.unshift({
17249                 tag :'span',
17250                 cls : 'input-group-addon input-group-prepend input-group-text',
17251                 html : this.before
17252             });
17253         }
17254         
17255         if(this.removable && !this.multiple){
17256             inputblock.cls += ' roo-removable';
17257             
17258             inputblock.cn.push({
17259                 tag: 'button',
17260                 html : 'x',
17261                 cls : 'roo-combo-removable-btn close'
17262             });
17263         }
17264
17265         if(this.hasFeedback && !this.allowBlank){
17266             
17267             inputblock.cls += ' has-feedback';
17268             
17269             inputblock.cn.push({
17270                 tag: 'span',
17271                 cls: 'glyphicon form-control-feedback'
17272             });
17273             
17274         }
17275         
17276         if (this.after) {
17277             
17278             inputblock.cls += (this.before) ? '' : ' input-group';
17279             
17280             inputblock.cn.push({
17281                 tag :'span',
17282                 cls : 'input-group-addon input-group-append input-group-text',
17283                 html : this.after
17284             });
17285         }
17286
17287         
17288         var ibwrap = inputblock;
17289         
17290         if(this.multiple){
17291             ibwrap = {
17292                 tag: 'ul',
17293                 cls: 'roo-select2-choices',
17294                 cn:[
17295                     {
17296                         tag: 'li',
17297                         cls: 'roo-select2-search-field',
17298                         cn: [
17299
17300                             inputblock
17301                         ]
17302                     }
17303                 ]
17304             };
17305         
17306             
17307         }
17308         
17309         var combobox = {
17310             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17311             cn: [
17312                 {
17313                     tag: 'input',
17314                     type : 'hidden',
17315                     cls: 'form-hidden-field'
17316                 },
17317                 ibwrap
17318             ]
17319         };
17320         
17321         if(!this.multiple && this.showToggleBtn){
17322             
17323             var caret = {
17324                 cls: 'caret'
17325             };
17326             
17327             if (this.caret != false) {
17328                 caret = {
17329                      tag: 'i',
17330                      cls: 'fa fa-' + this.caret
17331                 };
17332                 
17333             }
17334             
17335             combobox.cn.push({
17336                 tag :'span',
17337                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17338                 cn : [
17339                     Roo.bootstrap.version == 3 ? caret : '',
17340                     {
17341                         tag: 'span',
17342                         cls: 'combobox-clear',
17343                         cn  : [
17344                             {
17345                                 tag : 'i',
17346                                 cls: 'icon-remove'
17347                             }
17348                         ]
17349                     }
17350                 ]
17351
17352             })
17353         }
17354         
17355         if(this.multiple){
17356             combobox.cls += ' roo-select2-container-multi';
17357         }
17358         
17359         var align = this.labelAlign || this.parentLabelAlign();
17360         
17361         if (align ==='left' && this.fieldLabel.length) {
17362
17363             cfg.cn = [
17364                 {
17365                    tag : 'i',
17366                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17367                    tooltip : 'This field is required'
17368                 },
17369                 {
17370                     tag: 'label',
17371                     cls : 'control-label col-form-label',
17372                     html : this.fieldLabel
17373
17374                 },
17375                 {
17376                     cls : 'roo-combobox-wrap ', 
17377                     cn: [
17378                         combobox
17379                     ]
17380                 }
17381             ];
17382             
17383             var labelCfg = cfg.cn[1];
17384             var contentCfg = cfg.cn[2];
17385             
17386
17387             if(this.indicatorpos == 'right'){
17388                 cfg.cn = [
17389                     {
17390                         tag: 'label',
17391                         'for' :  id,
17392                         cls : 'control-label col-form-label',
17393                         cn : [
17394                             {
17395                                 tag : 'span',
17396                                 html : this.fieldLabel
17397                             },
17398                             {
17399                                 tag : 'i',
17400                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17401                                 tooltip : 'This field is required'
17402                             }
17403                         ]
17404                     },
17405                     {
17406                         cls : "roo-combobox-wrap ",
17407                         cn: [
17408                             combobox
17409                         ]
17410                     }
17411
17412                 ];
17413                 
17414                 labelCfg = cfg.cn[0];
17415                 contentCfg = cfg.cn[1];
17416             }
17417             
17418            
17419             
17420             if(this.labelWidth > 12){
17421                 labelCfg.style = "width: " + this.labelWidth + 'px';
17422             }
17423            
17424             if(this.labelWidth < 13 && this.labelmd == 0){
17425                 this.labelmd = this.labelWidth;
17426             }
17427             
17428             if(this.labellg > 0){
17429                 labelCfg.cls += ' col-lg-' + this.labellg;
17430                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17431             }
17432             
17433             if(this.labelmd > 0){
17434                 labelCfg.cls += ' col-md-' + this.labelmd;
17435                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17436             }
17437             
17438             if(this.labelsm > 0){
17439                 labelCfg.cls += ' col-sm-' + this.labelsm;
17440                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17441             }
17442             
17443             if(this.labelxs > 0){
17444                 labelCfg.cls += ' col-xs-' + this.labelxs;
17445                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17446             }
17447                 
17448                 
17449         } else if ( this.fieldLabel.length) {
17450             cfg.cn = [
17451                 {
17452                    tag : 'i',
17453                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17454                    tooltip : 'This field is required'
17455                 },
17456                 {
17457                     tag: 'label',
17458                     cls : 'control-label',
17459                     html : this.fieldLabel
17460
17461                 },
17462                 {
17463                     cls : '', 
17464                     cn: [
17465                         combobox
17466                     ]
17467                 }
17468             ];
17469             
17470             if(this.indicatorpos == 'right'){
17471                 cfg.cn = [
17472                     {
17473                         tag: 'label',
17474                         cls : 'control-label',
17475                         html : this.fieldLabel,
17476                         cn : [
17477                             {
17478                                tag : 'i',
17479                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17480                                tooltip : 'This field is required'
17481                             }
17482                         ]
17483                     },
17484                     {
17485                         cls : '', 
17486                         cn: [
17487                             combobox
17488                         ]
17489                     }
17490                 ];
17491             }
17492         } else {
17493             cfg.cn = combobox;    
17494         }
17495         
17496         
17497         var settings = this;
17498         
17499         ['xs','sm','md','lg'].map(function(size){
17500             if (settings[size]) {
17501                 cfg.cls += ' col-' + size + '-' + settings[size];
17502             }
17503         });
17504         
17505         return cfg;
17506     },
17507     
17508     initTouchView : function()
17509     {
17510         this.renderTouchView();
17511         
17512         this.touchViewEl.on('scroll', function(){
17513             this.el.dom.scrollTop = 0;
17514         }, this);
17515         
17516         this.originalValue = this.getValue();
17517         
17518         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17519         
17520         this.inputEl().on("click", this.showTouchView, this);
17521         if (this.triggerEl) {
17522             this.triggerEl.on("click", this.showTouchView, this);
17523         }
17524         
17525         
17526         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17527         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17528         
17529         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17530         
17531         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17532         this.store.on('load', this.onTouchViewLoad, this);
17533         this.store.on('loadexception', this.onTouchViewLoadException, this);
17534         
17535         if(this.hiddenName){
17536             
17537             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17538             
17539             this.hiddenField.dom.value =
17540                 this.hiddenValue !== undefined ? this.hiddenValue :
17541                 this.value !== undefined ? this.value : '';
17542         
17543             this.el.dom.removeAttribute('name');
17544             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17545         }
17546         
17547         if(this.multiple){
17548             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17549             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17550         }
17551         
17552         if(this.removable && !this.multiple){
17553             var close = this.closeTriggerEl();
17554             if(close){
17555                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17556                 close.on('click', this.removeBtnClick, this, close);
17557             }
17558         }
17559         /*
17560          * fix the bug in Safari iOS8
17561          */
17562         this.inputEl().on("focus", function(e){
17563             document.activeElement.blur();
17564         }, this);
17565         
17566         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17567         
17568         return;
17569         
17570         
17571     },
17572     
17573     renderTouchView : function()
17574     {
17575         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17576         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17577         
17578         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17579         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17580         
17581         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17582         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17583         this.touchViewBodyEl.setStyle('overflow', 'auto');
17584         
17585         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17586         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17587         
17588         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17589         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17590         
17591     },
17592     
17593     showTouchView : function()
17594     {
17595         if(this.disabled){
17596             return;
17597         }
17598         
17599         this.touchViewHeaderEl.hide();
17600
17601         if(this.modalTitle.length){
17602             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17603             this.touchViewHeaderEl.show();
17604         }
17605
17606         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17607         this.touchViewEl.show();
17608
17609         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17610         
17611         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17612         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17613
17614         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17615
17616         if(this.modalTitle.length){
17617             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17618         }
17619         
17620         this.touchViewBodyEl.setHeight(bodyHeight);
17621
17622         if(this.animate){
17623             var _this = this;
17624             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17625         }else{
17626             this.touchViewEl.addClass(['in','show']);
17627         }
17628         
17629         if(this._touchViewMask){
17630             Roo.get(document.body).addClass("x-body-masked");
17631             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17632             this._touchViewMask.setStyle('z-index', 10000);
17633             this._touchViewMask.addClass('show');
17634         }
17635         
17636         this.doTouchViewQuery();
17637         
17638     },
17639     
17640     hideTouchView : function()
17641     {
17642         this.touchViewEl.removeClass(['in','show']);
17643
17644         if(this.animate){
17645             var _this = this;
17646             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17647         }else{
17648             this.touchViewEl.setStyle('display', 'none');
17649         }
17650         
17651         if(this._touchViewMask){
17652             this._touchViewMask.removeClass('show');
17653             Roo.get(document.body).removeClass("x-body-masked");
17654         }
17655     },
17656     
17657     setTouchViewValue : function()
17658     {
17659         if(this.multiple){
17660             this.clearItem();
17661         
17662             var _this = this;
17663
17664             Roo.each(this.tickItems, function(o){
17665                 this.addItem(o);
17666             }, this);
17667         }
17668         
17669         this.hideTouchView();
17670     },
17671     
17672     doTouchViewQuery : function()
17673     {
17674         var qe = {
17675             query: '',
17676             forceAll: true,
17677             combo: this,
17678             cancel:false
17679         };
17680         
17681         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17682             return false;
17683         }
17684         
17685         if(!this.alwaysQuery || this.mode == 'local'){
17686             this.onTouchViewLoad();
17687             return;
17688         }
17689         
17690         this.store.load();
17691     },
17692     
17693     onTouchViewBeforeLoad : function(combo,opts)
17694     {
17695         return;
17696     },
17697
17698     // private
17699     onTouchViewLoad : function()
17700     {
17701         if(this.store.getCount() < 1){
17702             this.onTouchViewEmptyResults();
17703             return;
17704         }
17705         
17706         this.clearTouchView();
17707         
17708         var rawValue = this.getRawValue();
17709         
17710         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17711         
17712         this.tickItems = [];
17713         
17714         this.store.data.each(function(d, rowIndex){
17715             var row = this.touchViewListGroup.createChild(template);
17716             
17717             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17718                 row.addClass(d.data.cls);
17719             }
17720             
17721             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17722                 var cfg = {
17723                     data : d.data,
17724                     html : d.data[this.displayField]
17725                 };
17726                 
17727                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17728                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17729                 }
17730             }
17731             row.removeClass('selected');
17732             if(!this.multiple && this.valueField &&
17733                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17734             {
17735                 // radio buttons..
17736                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17737                 row.addClass('selected');
17738             }
17739             
17740             if(this.multiple && this.valueField &&
17741                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17742             {
17743                 
17744                 // checkboxes...
17745                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17746                 this.tickItems.push(d.data);
17747             }
17748             
17749             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17750             
17751         }, this);
17752         
17753         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17754         
17755         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17756
17757         if(this.modalTitle.length){
17758             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17759         }
17760
17761         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17762         
17763         if(this.mobile_restrict_height && listHeight < bodyHeight){
17764             this.touchViewBodyEl.setHeight(listHeight);
17765         }
17766         
17767         var _this = this;
17768         
17769         if(firstChecked && listHeight > bodyHeight){
17770             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17771         }
17772         
17773     },
17774     
17775     onTouchViewLoadException : function()
17776     {
17777         this.hideTouchView();
17778     },
17779     
17780     onTouchViewEmptyResults : function()
17781     {
17782         this.clearTouchView();
17783         
17784         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17785         
17786         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17787         
17788     },
17789     
17790     clearTouchView : function()
17791     {
17792         this.touchViewListGroup.dom.innerHTML = '';
17793     },
17794     
17795     onTouchViewClick : function(e, el, o)
17796     {
17797         e.preventDefault();
17798         
17799         var row = o.row;
17800         var rowIndex = o.rowIndex;
17801         
17802         var r = this.store.getAt(rowIndex);
17803         
17804         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17805             
17806             if(!this.multiple){
17807                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17808                     c.dom.removeAttribute('checked');
17809                 }, this);
17810
17811                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17812
17813                 this.setFromData(r.data);
17814
17815                 var close = this.closeTriggerEl();
17816
17817                 if(close){
17818                     close.show();
17819                 }
17820
17821                 this.hideTouchView();
17822
17823                 this.fireEvent('select', this, r, rowIndex);
17824
17825                 return;
17826             }
17827
17828             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17829                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17830                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17831                 return;
17832             }
17833
17834             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17835             this.addItem(r.data);
17836             this.tickItems.push(r.data);
17837         }
17838     },
17839     
17840     getAutoCreateNativeIOS : function()
17841     {
17842         var cfg = {
17843             cls: 'form-group' //input-group,
17844         };
17845         
17846         var combobox =  {
17847             tag: 'select',
17848             cls : 'roo-ios-select'
17849         };
17850         
17851         if (this.name) {
17852             combobox.name = this.name;
17853         }
17854         
17855         if (this.disabled) {
17856             combobox.disabled = true;
17857         }
17858         
17859         var settings = this;
17860         
17861         ['xs','sm','md','lg'].map(function(size){
17862             if (settings[size]) {
17863                 cfg.cls += ' col-' + size + '-' + settings[size];
17864             }
17865         });
17866         
17867         cfg.cn = combobox;
17868         
17869         return cfg;
17870         
17871     },
17872     
17873     initIOSView : function()
17874     {
17875         this.store.on('load', this.onIOSViewLoad, this);
17876         
17877         return;
17878     },
17879     
17880     onIOSViewLoad : function()
17881     {
17882         if(this.store.getCount() < 1){
17883             return;
17884         }
17885         
17886         this.clearIOSView();
17887         
17888         if(this.allowBlank) {
17889             
17890             var default_text = '-- SELECT --';
17891             
17892             if(this.placeholder.length){
17893                 default_text = this.placeholder;
17894             }
17895             
17896             if(this.emptyTitle.length){
17897                 default_text += ' - ' + this.emptyTitle + ' -';
17898             }
17899             
17900             var opt = this.inputEl().createChild({
17901                 tag: 'option',
17902                 value : 0,
17903                 html : default_text
17904             });
17905             
17906             var o = {};
17907             o[this.valueField] = 0;
17908             o[this.displayField] = default_text;
17909             
17910             this.ios_options.push({
17911                 data : o,
17912                 el : opt
17913             });
17914             
17915         }
17916         
17917         this.store.data.each(function(d, rowIndex){
17918             
17919             var html = '';
17920             
17921             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17922                 html = d.data[this.displayField];
17923             }
17924             
17925             var value = '';
17926             
17927             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17928                 value = d.data[this.valueField];
17929             }
17930             
17931             var option = {
17932                 tag: 'option',
17933                 value : value,
17934                 html : html
17935             };
17936             
17937             if(this.value == d.data[this.valueField]){
17938                 option['selected'] = true;
17939             }
17940             
17941             var opt = this.inputEl().createChild(option);
17942             
17943             this.ios_options.push({
17944                 data : d.data,
17945                 el : opt
17946             });
17947             
17948         }, this);
17949         
17950         this.inputEl().on('change', function(){
17951            this.fireEvent('select', this);
17952         }, this);
17953         
17954     },
17955     
17956     clearIOSView: function()
17957     {
17958         this.inputEl().dom.innerHTML = '';
17959         
17960         this.ios_options = [];
17961     },
17962     
17963     setIOSValue: function(v)
17964     {
17965         this.value = v;
17966         
17967         if(!this.ios_options){
17968             return;
17969         }
17970         
17971         Roo.each(this.ios_options, function(opts){
17972            
17973            opts.el.dom.removeAttribute('selected');
17974            
17975            if(opts.data[this.valueField] != v){
17976                return;
17977            }
17978            
17979            opts.el.dom.setAttribute('selected', true);
17980            
17981         }, this);
17982     }
17983
17984     /** 
17985     * @cfg {Boolean} grow 
17986     * @hide 
17987     */
17988     /** 
17989     * @cfg {Number} growMin 
17990     * @hide 
17991     */
17992     /** 
17993     * @cfg {Number} growMax 
17994     * @hide 
17995     */
17996     /**
17997      * @hide
17998      * @method autoSize
17999      */
18000 });
18001
18002 Roo.apply(Roo.bootstrap.ComboBox,  {
18003     
18004     header : {
18005         tag: 'div',
18006         cls: 'modal-header',
18007         cn: [
18008             {
18009                 tag: 'h4',
18010                 cls: 'modal-title'
18011             }
18012         ]
18013     },
18014     
18015     body : {
18016         tag: 'div',
18017         cls: 'modal-body',
18018         cn: [
18019             {
18020                 tag: 'ul',
18021                 cls: 'list-group'
18022             }
18023         ]
18024     },
18025     
18026     listItemRadio : {
18027         tag: 'li',
18028         cls: 'list-group-item',
18029         cn: [
18030             {
18031                 tag: 'span',
18032                 cls: 'roo-combobox-list-group-item-value'
18033             },
18034             {
18035                 tag: 'div',
18036                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18037                 cn: [
18038                     {
18039                         tag: 'input',
18040                         type: 'radio'
18041                     },
18042                     {
18043                         tag: 'label'
18044                     }
18045                 ]
18046             }
18047         ]
18048     },
18049     
18050     listItemCheckbox : {
18051         tag: 'li',
18052         cls: 'list-group-item',
18053         cn: [
18054             {
18055                 tag: 'span',
18056                 cls: 'roo-combobox-list-group-item-value'
18057             },
18058             {
18059                 tag: 'div',
18060                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18061                 cn: [
18062                     {
18063                         tag: 'input',
18064                         type: 'checkbox'
18065                     },
18066                     {
18067                         tag: 'label'
18068                     }
18069                 ]
18070             }
18071         ]
18072     },
18073     
18074     emptyResult : {
18075         tag: 'div',
18076         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18077     },
18078     
18079     footer : {
18080         tag: 'div',
18081         cls: 'modal-footer',
18082         cn: [
18083             {
18084                 tag: 'div',
18085                 cls: 'row',
18086                 cn: [
18087                     {
18088                         tag: 'div',
18089                         cls: 'col-xs-6 text-left',
18090                         cn: {
18091                             tag: 'button',
18092                             cls: 'btn btn-danger roo-touch-view-cancel',
18093                             html: 'Cancel'
18094                         }
18095                     },
18096                     {
18097                         tag: 'div',
18098                         cls: 'col-xs-6 text-right',
18099                         cn: {
18100                             tag: 'button',
18101                             cls: 'btn btn-success roo-touch-view-ok',
18102                             html: 'OK'
18103                         }
18104                     }
18105                 ]
18106             }
18107         ]
18108         
18109     }
18110 });
18111
18112 Roo.apply(Roo.bootstrap.ComboBox,  {
18113     
18114     touchViewTemplate : {
18115         tag: 'div',
18116         cls: 'modal fade roo-combobox-touch-view',
18117         cn: [
18118             {
18119                 tag: 'div',
18120                 cls: 'modal-dialog',
18121                 style : 'position:fixed', // we have to fix position....
18122                 cn: [
18123                     {
18124                         tag: 'div',
18125                         cls: 'modal-content',
18126                         cn: [
18127                             Roo.bootstrap.ComboBox.header,
18128                             Roo.bootstrap.ComboBox.body,
18129                             Roo.bootstrap.ComboBox.footer
18130                         ]
18131                     }
18132                 ]
18133             }
18134         ]
18135     }
18136 });/*
18137  * Based on:
18138  * Ext JS Library 1.1.1
18139  * Copyright(c) 2006-2007, Ext JS, LLC.
18140  *
18141  * Originally Released Under LGPL - original licence link has changed is not relivant.
18142  *
18143  * Fork - LGPL
18144  * <script type="text/javascript">
18145  */
18146
18147 /**
18148  * @class Roo.View
18149  * @extends Roo.util.Observable
18150  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18151  * This class also supports single and multi selection modes. <br>
18152  * Create a data model bound view:
18153  <pre><code>
18154  var store = new Roo.data.Store(...);
18155
18156  var view = new Roo.View({
18157     el : "my-element",
18158     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18159  
18160     singleSelect: true,
18161     selectedClass: "ydataview-selected",
18162     store: store
18163  });
18164
18165  // listen for node click?
18166  view.on("click", function(vw, index, node, e){
18167  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18168  });
18169
18170  // load XML data
18171  dataModel.load("foobar.xml");
18172  </code></pre>
18173  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18174  * <br><br>
18175  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18176  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18177  * 
18178  * Note: old style constructor is still suported (container, template, config)
18179  * 
18180  * @constructor
18181  * Create a new View
18182  * @param {Object} config The config object
18183  * 
18184  */
18185 Roo.View = function(config, depreciated_tpl, depreciated_config){
18186     
18187     this.parent = false;
18188     
18189     if (typeof(depreciated_tpl) == 'undefined') {
18190         // new way.. - universal constructor.
18191         Roo.apply(this, config);
18192         this.el  = Roo.get(this.el);
18193     } else {
18194         // old format..
18195         this.el  = Roo.get(config);
18196         this.tpl = depreciated_tpl;
18197         Roo.apply(this, depreciated_config);
18198     }
18199     this.wrapEl  = this.el.wrap().wrap();
18200     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18201     
18202     
18203     if(typeof(this.tpl) == "string"){
18204         this.tpl = new Roo.Template(this.tpl);
18205     } else {
18206         // support xtype ctors..
18207         this.tpl = new Roo.factory(this.tpl, Roo);
18208     }
18209     
18210     
18211     this.tpl.compile();
18212     
18213     /** @private */
18214     this.addEvents({
18215         /**
18216          * @event beforeclick
18217          * Fires before a click is processed. Returns false to cancel the default action.
18218          * @param {Roo.View} this
18219          * @param {Number} index The index of the target node
18220          * @param {HTMLElement} node The target node
18221          * @param {Roo.EventObject} e The raw event object
18222          */
18223             "beforeclick" : true,
18224         /**
18225          * @event click
18226          * Fires when a template node is clicked.
18227          * @param {Roo.View} this
18228          * @param {Number} index The index of the target node
18229          * @param {HTMLElement} node The target node
18230          * @param {Roo.EventObject} e The raw event object
18231          */
18232             "click" : true,
18233         /**
18234          * @event dblclick
18235          * Fires when a template node is double clicked.
18236          * @param {Roo.View} this
18237          * @param {Number} index The index of the target node
18238          * @param {HTMLElement} node The target node
18239          * @param {Roo.EventObject} e The raw event object
18240          */
18241             "dblclick" : true,
18242         /**
18243          * @event contextmenu
18244          * Fires when a template node is right clicked.
18245          * @param {Roo.View} this
18246          * @param {Number} index The index of the target node
18247          * @param {HTMLElement} node The target node
18248          * @param {Roo.EventObject} e The raw event object
18249          */
18250             "contextmenu" : true,
18251         /**
18252          * @event selectionchange
18253          * Fires when the selected nodes change.
18254          * @param {Roo.View} this
18255          * @param {Array} selections Array of the selected nodes
18256          */
18257             "selectionchange" : true,
18258     
18259         /**
18260          * @event beforeselect
18261          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18262          * @param {Roo.View} this
18263          * @param {HTMLElement} node The node to be selected
18264          * @param {Array} selections Array of currently selected nodes
18265          */
18266             "beforeselect" : true,
18267         /**
18268          * @event preparedata
18269          * Fires on every row to render, to allow you to change the data.
18270          * @param {Roo.View} this
18271          * @param {Object} data to be rendered (change this)
18272          */
18273           "preparedata" : true
18274           
18275           
18276         });
18277
18278
18279
18280     this.el.on({
18281         "click": this.onClick,
18282         "dblclick": this.onDblClick,
18283         "contextmenu": this.onContextMenu,
18284         scope:this
18285     });
18286
18287     this.selections = [];
18288     this.nodes = [];
18289     this.cmp = new Roo.CompositeElementLite([]);
18290     if(this.store){
18291         this.store = Roo.factory(this.store, Roo.data);
18292         this.setStore(this.store, true);
18293     }
18294     
18295     if ( this.footer && this.footer.xtype) {
18296            
18297          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18298         
18299         this.footer.dataSource = this.store;
18300         this.footer.container = fctr;
18301         this.footer = Roo.factory(this.footer, Roo);
18302         fctr.insertFirst(this.el);
18303         
18304         // this is a bit insane - as the paging toolbar seems to detach the el..
18305 //        dom.parentNode.parentNode.parentNode
18306          // they get detached?
18307     }
18308     
18309     
18310     Roo.View.superclass.constructor.call(this);
18311     
18312     
18313 };
18314
18315 Roo.extend(Roo.View, Roo.util.Observable, {
18316     
18317      /**
18318      * @cfg {Roo.data.Store} store Data store to load data from.
18319      */
18320     store : false,
18321     
18322     /**
18323      * @cfg {String|Roo.Element} el The container element.
18324      */
18325     el : '',
18326     
18327     /**
18328      * @cfg {String|Roo.Template} tpl The template used by this View 
18329      */
18330     tpl : false,
18331     /**
18332      * @cfg {String} dataName the named area of the template to use as the data area
18333      *                          Works with domtemplates roo-name="name"
18334      */
18335     dataName: false,
18336     /**
18337      * @cfg {String} selectedClass The css class to add to selected nodes
18338      */
18339     selectedClass : "x-view-selected",
18340      /**
18341      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18342      */
18343     emptyText : "",
18344     
18345     /**
18346      * @cfg {String} text to display on mask (default Loading)
18347      */
18348     mask : false,
18349     /**
18350      * @cfg {Boolean} multiSelect Allow multiple selection
18351      */
18352     multiSelect : false,
18353     /**
18354      * @cfg {Boolean} singleSelect Allow single selection
18355      */
18356     singleSelect:  false,
18357     
18358     /**
18359      * @cfg {Boolean} toggleSelect - selecting 
18360      */
18361     toggleSelect : false,
18362     
18363     /**
18364      * @cfg {Boolean} tickable - selecting 
18365      */
18366     tickable : false,
18367     
18368     /**
18369      * Returns the element this view is bound to.
18370      * @return {Roo.Element}
18371      */
18372     getEl : function(){
18373         return this.wrapEl;
18374     },
18375     
18376     
18377
18378     /**
18379      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18380      */
18381     refresh : function(){
18382         //Roo.log('refresh');
18383         var t = this.tpl;
18384         
18385         // if we are using something like 'domtemplate', then
18386         // the what gets used is:
18387         // t.applySubtemplate(NAME, data, wrapping data..)
18388         // the outer template then get' applied with
18389         //     the store 'extra data'
18390         // and the body get's added to the
18391         //      roo-name="data" node?
18392         //      <span class='roo-tpl-{name}'></span> ?????
18393         
18394         
18395         
18396         this.clearSelections();
18397         this.el.update("");
18398         var html = [];
18399         var records = this.store.getRange();
18400         if(records.length < 1) {
18401             
18402             // is this valid??  = should it render a template??
18403             
18404             this.el.update(this.emptyText);
18405             return;
18406         }
18407         var el = this.el;
18408         if (this.dataName) {
18409             this.el.update(t.apply(this.store.meta)); //????
18410             el = this.el.child('.roo-tpl-' + this.dataName);
18411         }
18412         
18413         for(var i = 0, len = records.length; i < len; i++){
18414             var data = this.prepareData(records[i].data, i, records[i]);
18415             this.fireEvent("preparedata", this, data, i, records[i]);
18416             
18417             var d = Roo.apply({}, data);
18418             
18419             if(this.tickable){
18420                 Roo.apply(d, {'roo-id' : Roo.id()});
18421                 
18422                 var _this = this;
18423             
18424                 Roo.each(this.parent.item, function(item){
18425                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18426                         return;
18427                     }
18428                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18429                 });
18430             }
18431             
18432             html[html.length] = Roo.util.Format.trim(
18433                 this.dataName ?
18434                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18435                     t.apply(d)
18436             );
18437         }
18438         
18439         
18440         
18441         el.update(html.join(""));
18442         this.nodes = el.dom.childNodes;
18443         this.updateIndexes(0);
18444     },
18445     
18446
18447     /**
18448      * Function to override to reformat the data that is sent to
18449      * the template for each node.
18450      * DEPRICATED - use the preparedata event handler.
18451      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18452      * a JSON object for an UpdateManager bound view).
18453      */
18454     prepareData : function(data, index, record)
18455     {
18456         this.fireEvent("preparedata", this, data, index, record);
18457         return data;
18458     },
18459
18460     onUpdate : function(ds, record){
18461         // Roo.log('on update');   
18462         this.clearSelections();
18463         var index = this.store.indexOf(record);
18464         var n = this.nodes[index];
18465         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18466         n.parentNode.removeChild(n);
18467         this.updateIndexes(index, index);
18468     },
18469
18470     
18471     
18472 // --------- FIXME     
18473     onAdd : function(ds, records, index)
18474     {
18475         //Roo.log(['on Add', ds, records, index] );        
18476         this.clearSelections();
18477         if(this.nodes.length == 0){
18478             this.refresh();
18479             return;
18480         }
18481         var n = this.nodes[index];
18482         for(var i = 0, len = records.length; i < len; i++){
18483             var d = this.prepareData(records[i].data, i, records[i]);
18484             if(n){
18485                 this.tpl.insertBefore(n, d);
18486             }else{
18487                 
18488                 this.tpl.append(this.el, d);
18489             }
18490         }
18491         this.updateIndexes(index);
18492     },
18493
18494     onRemove : function(ds, record, index){
18495        // Roo.log('onRemove');
18496         this.clearSelections();
18497         var el = this.dataName  ?
18498             this.el.child('.roo-tpl-' + this.dataName) :
18499             this.el; 
18500         
18501         el.dom.removeChild(this.nodes[index]);
18502         this.updateIndexes(index);
18503     },
18504
18505     /**
18506      * Refresh an individual node.
18507      * @param {Number} index
18508      */
18509     refreshNode : function(index){
18510         this.onUpdate(this.store, this.store.getAt(index));
18511     },
18512
18513     updateIndexes : function(startIndex, endIndex){
18514         var ns = this.nodes;
18515         startIndex = startIndex || 0;
18516         endIndex = endIndex || ns.length - 1;
18517         for(var i = startIndex; i <= endIndex; i++){
18518             ns[i].nodeIndex = i;
18519         }
18520     },
18521
18522     /**
18523      * Changes the data store this view uses and refresh the view.
18524      * @param {Store} store
18525      */
18526     setStore : function(store, initial){
18527         if(!initial && this.store){
18528             this.store.un("datachanged", this.refresh);
18529             this.store.un("add", this.onAdd);
18530             this.store.un("remove", this.onRemove);
18531             this.store.un("update", this.onUpdate);
18532             this.store.un("clear", this.refresh);
18533             this.store.un("beforeload", this.onBeforeLoad);
18534             this.store.un("load", this.onLoad);
18535             this.store.un("loadexception", this.onLoad);
18536         }
18537         if(store){
18538           
18539             store.on("datachanged", this.refresh, this);
18540             store.on("add", this.onAdd, this);
18541             store.on("remove", this.onRemove, this);
18542             store.on("update", this.onUpdate, this);
18543             store.on("clear", this.refresh, this);
18544             store.on("beforeload", this.onBeforeLoad, this);
18545             store.on("load", this.onLoad, this);
18546             store.on("loadexception", this.onLoad, this);
18547         }
18548         
18549         if(store){
18550             this.refresh();
18551         }
18552     },
18553     /**
18554      * onbeforeLoad - masks the loading area.
18555      *
18556      */
18557     onBeforeLoad : function(store,opts)
18558     {
18559          //Roo.log('onBeforeLoad');   
18560         if (!opts.add) {
18561             this.el.update("");
18562         }
18563         this.el.mask(this.mask ? this.mask : "Loading" ); 
18564     },
18565     onLoad : function ()
18566     {
18567         this.el.unmask();
18568     },
18569     
18570
18571     /**
18572      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18573      * @param {HTMLElement} node
18574      * @return {HTMLElement} The template node
18575      */
18576     findItemFromChild : function(node){
18577         var el = this.dataName  ?
18578             this.el.child('.roo-tpl-' + this.dataName,true) :
18579             this.el.dom; 
18580         
18581         if(!node || node.parentNode == el){
18582                     return node;
18583             }
18584             var p = node.parentNode;
18585             while(p && p != el){
18586             if(p.parentNode == el){
18587                 return p;
18588             }
18589             p = p.parentNode;
18590         }
18591             return null;
18592     },
18593
18594     /** @ignore */
18595     onClick : function(e){
18596         var item = this.findItemFromChild(e.getTarget());
18597         if(item){
18598             var index = this.indexOf(item);
18599             if(this.onItemClick(item, index, e) !== false){
18600                 this.fireEvent("click", this, index, item, e);
18601             }
18602         }else{
18603             this.clearSelections();
18604         }
18605     },
18606
18607     /** @ignore */
18608     onContextMenu : function(e){
18609         var item = this.findItemFromChild(e.getTarget());
18610         if(item){
18611             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18612         }
18613     },
18614
18615     /** @ignore */
18616     onDblClick : function(e){
18617         var item = this.findItemFromChild(e.getTarget());
18618         if(item){
18619             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18620         }
18621     },
18622
18623     onItemClick : function(item, index, e)
18624     {
18625         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18626             return false;
18627         }
18628         if (this.toggleSelect) {
18629             var m = this.isSelected(item) ? 'unselect' : 'select';
18630             //Roo.log(m);
18631             var _t = this;
18632             _t[m](item, true, false);
18633             return true;
18634         }
18635         if(this.multiSelect || this.singleSelect){
18636             if(this.multiSelect && e.shiftKey && this.lastSelection){
18637                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18638             }else{
18639                 this.select(item, this.multiSelect && e.ctrlKey);
18640                 this.lastSelection = item;
18641             }
18642             
18643             if(!this.tickable){
18644                 e.preventDefault();
18645             }
18646             
18647         }
18648         return true;
18649     },
18650
18651     /**
18652      * Get the number of selected nodes.
18653      * @return {Number}
18654      */
18655     getSelectionCount : function(){
18656         return this.selections.length;
18657     },
18658
18659     /**
18660      * Get the currently selected nodes.
18661      * @return {Array} An array of HTMLElements
18662      */
18663     getSelectedNodes : function(){
18664         return this.selections;
18665     },
18666
18667     /**
18668      * Get the indexes of the selected nodes.
18669      * @return {Array}
18670      */
18671     getSelectedIndexes : function(){
18672         var indexes = [], s = this.selections;
18673         for(var i = 0, len = s.length; i < len; i++){
18674             indexes.push(s[i].nodeIndex);
18675         }
18676         return indexes;
18677     },
18678
18679     /**
18680      * Clear all selections
18681      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18682      */
18683     clearSelections : function(suppressEvent){
18684         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18685             this.cmp.elements = this.selections;
18686             this.cmp.removeClass(this.selectedClass);
18687             this.selections = [];
18688             if(!suppressEvent){
18689                 this.fireEvent("selectionchange", this, this.selections);
18690             }
18691         }
18692     },
18693
18694     /**
18695      * Returns true if the passed node is selected
18696      * @param {HTMLElement/Number} node The node or node index
18697      * @return {Boolean}
18698      */
18699     isSelected : function(node){
18700         var s = this.selections;
18701         if(s.length < 1){
18702             return false;
18703         }
18704         node = this.getNode(node);
18705         return s.indexOf(node) !== -1;
18706     },
18707
18708     /**
18709      * Selects nodes.
18710      * @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
18711      * @param {Boolean} keepExisting (optional) true to keep existing selections
18712      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18713      */
18714     select : function(nodeInfo, keepExisting, suppressEvent){
18715         if(nodeInfo instanceof Array){
18716             if(!keepExisting){
18717                 this.clearSelections(true);
18718             }
18719             for(var i = 0, len = nodeInfo.length; i < len; i++){
18720                 this.select(nodeInfo[i], true, true);
18721             }
18722             return;
18723         } 
18724         var node = this.getNode(nodeInfo);
18725         if(!node || this.isSelected(node)){
18726             return; // already selected.
18727         }
18728         if(!keepExisting){
18729             this.clearSelections(true);
18730         }
18731         
18732         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18733             Roo.fly(node).addClass(this.selectedClass);
18734             this.selections.push(node);
18735             if(!suppressEvent){
18736                 this.fireEvent("selectionchange", this, this.selections);
18737             }
18738         }
18739         
18740         
18741     },
18742       /**
18743      * Unselects nodes.
18744      * @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
18745      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18746      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18747      */
18748     unselect : function(nodeInfo, keepExisting, suppressEvent)
18749     {
18750         if(nodeInfo instanceof Array){
18751             Roo.each(this.selections, function(s) {
18752                 this.unselect(s, nodeInfo);
18753             }, this);
18754             return;
18755         }
18756         var node = this.getNode(nodeInfo);
18757         if(!node || !this.isSelected(node)){
18758             //Roo.log("not selected");
18759             return; // not selected.
18760         }
18761         // fireevent???
18762         var ns = [];
18763         Roo.each(this.selections, function(s) {
18764             if (s == node ) {
18765                 Roo.fly(node).removeClass(this.selectedClass);
18766
18767                 return;
18768             }
18769             ns.push(s);
18770         },this);
18771         
18772         this.selections= ns;
18773         this.fireEvent("selectionchange", this, this.selections);
18774     },
18775
18776     /**
18777      * Gets a template node.
18778      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18779      * @return {HTMLElement} The node or null if it wasn't found
18780      */
18781     getNode : function(nodeInfo){
18782         if(typeof nodeInfo == "string"){
18783             return document.getElementById(nodeInfo);
18784         }else if(typeof nodeInfo == "number"){
18785             return this.nodes[nodeInfo];
18786         }
18787         return nodeInfo;
18788     },
18789
18790     /**
18791      * Gets a range template nodes.
18792      * @param {Number} startIndex
18793      * @param {Number} endIndex
18794      * @return {Array} An array of nodes
18795      */
18796     getNodes : function(start, end){
18797         var ns = this.nodes;
18798         start = start || 0;
18799         end = typeof end == "undefined" ? ns.length - 1 : end;
18800         var nodes = [];
18801         if(start <= end){
18802             for(var i = start; i <= end; i++){
18803                 nodes.push(ns[i]);
18804             }
18805         } else{
18806             for(var i = start; i >= end; i--){
18807                 nodes.push(ns[i]);
18808             }
18809         }
18810         return nodes;
18811     },
18812
18813     /**
18814      * Finds the index of the passed node
18815      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18816      * @return {Number} The index of the node or -1
18817      */
18818     indexOf : function(node){
18819         node = this.getNode(node);
18820         if(typeof node.nodeIndex == "number"){
18821             return node.nodeIndex;
18822         }
18823         var ns = this.nodes;
18824         for(var i = 0, len = ns.length; i < len; i++){
18825             if(ns[i] == node){
18826                 return i;
18827             }
18828         }
18829         return -1;
18830     }
18831 });
18832 /*
18833  * - LGPL
18834  *
18835  * based on jquery fullcalendar
18836  * 
18837  */
18838
18839 Roo.bootstrap = Roo.bootstrap || {};
18840 /**
18841  * @class Roo.bootstrap.Calendar
18842  * @extends Roo.bootstrap.Component
18843  * Bootstrap Calendar class
18844  * @cfg {Boolean} loadMask (true|false) default false
18845  * @cfg {Object} header generate the user specific header of the calendar, default false
18846
18847  * @constructor
18848  * Create a new Container
18849  * @param {Object} config The config object
18850  */
18851
18852
18853
18854 Roo.bootstrap.Calendar = function(config){
18855     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18856      this.addEvents({
18857         /**
18858              * @event select
18859              * Fires when a date is selected
18860              * @param {DatePicker} this
18861              * @param {Date} date The selected date
18862              */
18863         'select': true,
18864         /**
18865              * @event monthchange
18866              * Fires when the displayed month changes 
18867              * @param {DatePicker} this
18868              * @param {Date} date The selected month
18869              */
18870         'monthchange': true,
18871         /**
18872              * @event evententer
18873              * Fires when mouse over an event
18874              * @param {Calendar} this
18875              * @param {event} Event
18876              */
18877         'evententer': true,
18878         /**
18879              * @event eventleave
18880              * Fires when the mouse leaves an
18881              * @param {Calendar} this
18882              * @param {event}
18883              */
18884         'eventleave': true,
18885         /**
18886              * @event eventclick
18887              * Fires when the mouse click an
18888              * @param {Calendar} this
18889              * @param {event}
18890              */
18891         'eventclick': true
18892         
18893     });
18894
18895 };
18896
18897 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18898     
18899      /**
18900      * @cfg {Number} startDay
18901      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18902      */
18903     startDay : 0,
18904     
18905     loadMask : false,
18906     
18907     header : false,
18908       
18909     getAutoCreate : function(){
18910         
18911         
18912         var fc_button = function(name, corner, style, content ) {
18913             return Roo.apply({},{
18914                 tag : 'span',
18915                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18916                          (corner.length ?
18917                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18918                             ''
18919                         ),
18920                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18921                 unselectable: 'on'
18922             });
18923         };
18924         
18925         var header = {};
18926         
18927         if(!this.header){
18928             header = {
18929                 tag : 'table',
18930                 cls : 'fc-header',
18931                 style : 'width:100%',
18932                 cn : [
18933                     {
18934                         tag: 'tr',
18935                         cn : [
18936                             {
18937                                 tag : 'td',
18938                                 cls : 'fc-header-left',
18939                                 cn : [
18940                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18941                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18942                                     { tag: 'span', cls: 'fc-header-space' },
18943                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18944
18945
18946                                 ]
18947                             },
18948
18949                             {
18950                                 tag : 'td',
18951                                 cls : 'fc-header-center',
18952                                 cn : [
18953                                     {
18954                                         tag: 'span',
18955                                         cls: 'fc-header-title',
18956                                         cn : {
18957                                             tag: 'H2',
18958                                             html : 'month / year'
18959                                         }
18960                                     }
18961
18962                                 ]
18963                             },
18964                             {
18965                                 tag : 'td',
18966                                 cls : 'fc-header-right',
18967                                 cn : [
18968                               /*      fc_button('month', 'left', '', 'month' ),
18969                                     fc_button('week', '', '', 'week' ),
18970                                     fc_button('day', 'right', '', 'day' )
18971                                 */    
18972
18973                                 ]
18974                             }
18975
18976                         ]
18977                     }
18978                 ]
18979             };
18980         }
18981         
18982         header = this.header;
18983         
18984        
18985         var cal_heads = function() {
18986             var ret = [];
18987             // fixme - handle this.
18988             
18989             for (var i =0; i < Date.dayNames.length; i++) {
18990                 var d = Date.dayNames[i];
18991                 ret.push({
18992                     tag: 'th',
18993                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18994                     html : d.substring(0,3)
18995                 });
18996                 
18997             }
18998             ret[0].cls += ' fc-first';
18999             ret[6].cls += ' fc-last';
19000             return ret;
19001         };
19002         var cal_cell = function(n) {
19003             return  {
19004                 tag: 'td',
19005                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19006                 cn : [
19007                     {
19008                         cn : [
19009                             {
19010                                 cls: 'fc-day-number',
19011                                 html: 'D'
19012                             },
19013                             {
19014                                 cls: 'fc-day-content',
19015                              
19016                                 cn : [
19017                                      {
19018                                         style: 'position: relative;' // height: 17px;
19019                                     }
19020                                 ]
19021                             }
19022                             
19023                             
19024                         ]
19025                     }
19026                 ]
19027                 
19028             }
19029         };
19030         var cal_rows = function() {
19031             
19032             var ret = [];
19033             for (var r = 0; r < 6; r++) {
19034                 var row= {
19035                     tag : 'tr',
19036                     cls : 'fc-week',
19037                     cn : []
19038                 };
19039                 
19040                 for (var i =0; i < Date.dayNames.length; i++) {
19041                     var d = Date.dayNames[i];
19042                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19043
19044                 }
19045                 row.cn[0].cls+=' fc-first';
19046                 row.cn[0].cn[0].style = 'min-height:90px';
19047                 row.cn[6].cls+=' fc-last';
19048                 ret.push(row);
19049                 
19050             }
19051             ret[0].cls += ' fc-first';
19052             ret[4].cls += ' fc-prev-last';
19053             ret[5].cls += ' fc-last';
19054             return ret;
19055             
19056         };
19057         
19058         var cal_table = {
19059             tag: 'table',
19060             cls: 'fc-border-separate',
19061             style : 'width:100%',
19062             cellspacing  : 0,
19063             cn : [
19064                 { 
19065                     tag: 'thead',
19066                     cn : [
19067                         { 
19068                             tag: 'tr',
19069                             cls : 'fc-first fc-last',
19070                             cn : cal_heads()
19071                         }
19072                     ]
19073                 },
19074                 { 
19075                     tag: 'tbody',
19076                     cn : cal_rows()
19077                 }
19078                   
19079             ]
19080         };
19081          
19082          var cfg = {
19083             cls : 'fc fc-ltr',
19084             cn : [
19085                 header,
19086                 {
19087                     cls : 'fc-content',
19088                     style : "position: relative;",
19089                     cn : [
19090                         {
19091                             cls : 'fc-view fc-view-month fc-grid',
19092                             style : 'position: relative',
19093                             unselectable : 'on',
19094                             cn : [
19095                                 {
19096                                     cls : 'fc-event-container',
19097                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19098                                 },
19099                                 cal_table
19100                             ]
19101                         }
19102                     ]
19103     
19104                 }
19105            ] 
19106             
19107         };
19108         
19109          
19110         
19111         return cfg;
19112     },
19113     
19114     
19115     initEvents : function()
19116     {
19117         if(!this.store){
19118             throw "can not find store for calendar";
19119         }
19120         
19121         var mark = {
19122             tag: "div",
19123             cls:"x-dlg-mask",
19124             style: "text-align:center",
19125             cn: [
19126                 {
19127                     tag: "div",
19128                     style: "background-color:white;width:50%;margin:250 auto",
19129                     cn: [
19130                         {
19131                             tag: "img",
19132                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19133                         },
19134                         {
19135                             tag: "span",
19136                             html: "Loading"
19137                         }
19138                         
19139                     ]
19140                 }
19141             ]
19142         };
19143         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19144         
19145         var size = this.el.select('.fc-content', true).first().getSize();
19146         this.maskEl.setSize(size.width, size.height);
19147         this.maskEl.enableDisplayMode("block");
19148         if(!this.loadMask){
19149             this.maskEl.hide();
19150         }
19151         
19152         this.store = Roo.factory(this.store, Roo.data);
19153         this.store.on('load', this.onLoad, this);
19154         this.store.on('beforeload', this.onBeforeLoad, this);
19155         
19156         this.resize();
19157         
19158         this.cells = this.el.select('.fc-day',true);
19159         //Roo.log(this.cells);
19160         this.textNodes = this.el.query('.fc-day-number');
19161         this.cells.addClassOnOver('fc-state-hover');
19162         
19163         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19164         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19165         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19166         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19167         
19168         this.on('monthchange', this.onMonthChange, this);
19169         
19170         this.update(new Date().clearTime());
19171     },
19172     
19173     resize : function() {
19174         var sz  = this.el.getSize();
19175         
19176         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19177         this.el.select('.fc-day-content div',true).setHeight(34);
19178     },
19179     
19180     
19181     // private
19182     showPrevMonth : function(e){
19183         this.update(this.activeDate.add("mo", -1));
19184     },
19185     showToday : function(e){
19186         this.update(new Date().clearTime());
19187     },
19188     // private
19189     showNextMonth : function(e){
19190         this.update(this.activeDate.add("mo", 1));
19191     },
19192
19193     // private
19194     showPrevYear : function(){
19195         this.update(this.activeDate.add("y", -1));
19196     },
19197
19198     // private
19199     showNextYear : function(){
19200         this.update(this.activeDate.add("y", 1));
19201     },
19202
19203     
19204    // private
19205     update : function(date)
19206     {
19207         var vd = this.activeDate;
19208         this.activeDate = date;
19209 //        if(vd && this.el){
19210 //            var t = date.getTime();
19211 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19212 //                Roo.log('using add remove');
19213 //                
19214 //                this.fireEvent('monthchange', this, date);
19215 //                
19216 //                this.cells.removeClass("fc-state-highlight");
19217 //                this.cells.each(function(c){
19218 //                   if(c.dateValue == t){
19219 //                       c.addClass("fc-state-highlight");
19220 //                       setTimeout(function(){
19221 //                            try{c.dom.firstChild.focus();}catch(e){}
19222 //                       }, 50);
19223 //                       return false;
19224 //                   }
19225 //                   return true;
19226 //                });
19227 //                return;
19228 //            }
19229 //        }
19230         
19231         var days = date.getDaysInMonth();
19232         
19233         var firstOfMonth = date.getFirstDateOfMonth();
19234         var startingPos = firstOfMonth.getDay()-this.startDay;
19235         
19236         if(startingPos < this.startDay){
19237             startingPos += 7;
19238         }
19239         
19240         var pm = date.add(Date.MONTH, -1);
19241         var prevStart = pm.getDaysInMonth()-startingPos;
19242 //        
19243         this.cells = this.el.select('.fc-day',true);
19244         this.textNodes = this.el.query('.fc-day-number');
19245         this.cells.addClassOnOver('fc-state-hover');
19246         
19247         var cells = this.cells.elements;
19248         var textEls = this.textNodes;
19249         
19250         Roo.each(cells, function(cell){
19251             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19252         });
19253         
19254         days += startingPos;
19255
19256         // convert everything to numbers so it's fast
19257         var day = 86400000;
19258         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19259         //Roo.log(d);
19260         //Roo.log(pm);
19261         //Roo.log(prevStart);
19262         
19263         var today = new Date().clearTime().getTime();
19264         var sel = date.clearTime().getTime();
19265         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19266         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19267         var ddMatch = this.disabledDatesRE;
19268         var ddText = this.disabledDatesText;
19269         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19270         var ddaysText = this.disabledDaysText;
19271         var format = this.format;
19272         
19273         var setCellClass = function(cal, cell){
19274             cell.row = 0;
19275             cell.events = [];
19276             cell.more = [];
19277             //Roo.log('set Cell Class');
19278             cell.title = "";
19279             var t = d.getTime();
19280             
19281             //Roo.log(d);
19282             
19283             cell.dateValue = t;
19284             if(t == today){
19285                 cell.className += " fc-today";
19286                 cell.className += " fc-state-highlight";
19287                 cell.title = cal.todayText;
19288             }
19289             if(t == sel){
19290                 // disable highlight in other month..
19291                 //cell.className += " fc-state-highlight";
19292                 
19293             }
19294             // disabling
19295             if(t < min) {
19296                 cell.className = " fc-state-disabled";
19297                 cell.title = cal.minText;
19298                 return;
19299             }
19300             if(t > max) {
19301                 cell.className = " fc-state-disabled";
19302                 cell.title = cal.maxText;
19303                 return;
19304             }
19305             if(ddays){
19306                 if(ddays.indexOf(d.getDay()) != -1){
19307                     cell.title = ddaysText;
19308                     cell.className = " fc-state-disabled";
19309                 }
19310             }
19311             if(ddMatch && format){
19312                 var fvalue = d.dateFormat(format);
19313                 if(ddMatch.test(fvalue)){
19314                     cell.title = ddText.replace("%0", fvalue);
19315                     cell.className = " fc-state-disabled";
19316                 }
19317             }
19318             
19319             if (!cell.initialClassName) {
19320                 cell.initialClassName = cell.dom.className;
19321             }
19322             
19323             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19324         };
19325
19326         var i = 0;
19327         
19328         for(; i < startingPos; i++) {
19329             textEls[i].innerHTML = (++prevStart);
19330             d.setDate(d.getDate()+1);
19331             
19332             cells[i].className = "fc-past fc-other-month";
19333             setCellClass(this, cells[i]);
19334         }
19335         
19336         var intDay = 0;
19337         
19338         for(; i < days; i++){
19339             intDay = i - startingPos + 1;
19340             textEls[i].innerHTML = (intDay);
19341             d.setDate(d.getDate()+1);
19342             
19343             cells[i].className = ''; // "x-date-active";
19344             setCellClass(this, cells[i]);
19345         }
19346         var extraDays = 0;
19347         
19348         for(; i < 42; i++) {
19349             textEls[i].innerHTML = (++extraDays);
19350             d.setDate(d.getDate()+1);
19351             
19352             cells[i].className = "fc-future fc-other-month";
19353             setCellClass(this, cells[i]);
19354         }
19355         
19356         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19357         
19358         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19359         
19360         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19361         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19362         
19363         if(totalRows != 6){
19364             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19365             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19366         }
19367         
19368         this.fireEvent('monthchange', this, date);
19369         
19370         
19371         /*
19372         if(!this.internalRender){
19373             var main = this.el.dom.firstChild;
19374             var w = main.offsetWidth;
19375             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19376             Roo.fly(main).setWidth(w);
19377             this.internalRender = true;
19378             // opera does not respect the auto grow header center column
19379             // then, after it gets a width opera refuses to recalculate
19380             // without a second pass
19381             if(Roo.isOpera && !this.secondPass){
19382                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19383                 this.secondPass = true;
19384                 this.update.defer(10, this, [date]);
19385             }
19386         }
19387         */
19388         
19389     },
19390     
19391     findCell : function(dt) {
19392         dt = dt.clearTime().getTime();
19393         var ret = false;
19394         this.cells.each(function(c){
19395             //Roo.log("check " +c.dateValue + '?=' + dt);
19396             if(c.dateValue == dt){
19397                 ret = c;
19398                 return false;
19399             }
19400             return true;
19401         });
19402         
19403         return ret;
19404     },
19405     
19406     findCells : function(ev) {
19407         var s = ev.start.clone().clearTime().getTime();
19408        // Roo.log(s);
19409         var e= ev.end.clone().clearTime().getTime();
19410        // Roo.log(e);
19411         var ret = [];
19412         this.cells.each(function(c){
19413              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19414             
19415             if(c.dateValue > e){
19416                 return ;
19417             }
19418             if(c.dateValue < s){
19419                 return ;
19420             }
19421             ret.push(c);
19422         });
19423         
19424         return ret;    
19425     },
19426     
19427 //    findBestRow: function(cells)
19428 //    {
19429 //        var ret = 0;
19430 //        
19431 //        for (var i =0 ; i < cells.length;i++) {
19432 //            ret  = Math.max(cells[i].rows || 0,ret);
19433 //        }
19434 //        return ret;
19435 //        
19436 //    },
19437     
19438     
19439     addItem : function(ev)
19440     {
19441         // look for vertical location slot in
19442         var cells = this.findCells(ev);
19443         
19444 //        ev.row = this.findBestRow(cells);
19445         
19446         // work out the location.
19447         
19448         var crow = false;
19449         var rows = [];
19450         for(var i =0; i < cells.length; i++) {
19451             
19452             cells[i].row = cells[0].row;
19453             
19454             if(i == 0){
19455                 cells[i].row = cells[i].row + 1;
19456             }
19457             
19458             if (!crow) {
19459                 crow = {
19460                     start : cells[i],
19461                     end :  cells[i]
19462                 };
19463                 continue;
19464             }
19465             if (crow.start.getY() == cells[i].getY()) {
19466                 // on same row.
19467                 crow.end = cells[i];
19468                 continue;
19469             }
19470             // different row.
19471             rows.push(crow);
19472             crow = {
19473                 start: cells[i],
19474                 end : cells[i]
19475             };
19476             
19477         }
19478         
19479         rows.push(crow);
19480         ev.els = [];
19481         ev.rows = rows;
19482         ev.cells = cells;
19483         
19484         cells[0].events.push(ev);
19485         
19486         this.calevents.push(ev);
19487     },
19488     
19489     clearEvents: function() {
19490         
19491         if(!this.calevents){
19492             return;
19493         }
19494         
19495         Roo.each(this.cells.elements, function(c){
19496             c.row = 0;
19497             c.events = [];
19498             c.more = [];
19499         });
19500         
19501         Roo.each(this.calevents, function(e) {
19502             Roo.each(e.els, function(el) {
19503                 el.un('mouseenter' ,this.onEventEnter, this);
19504                 el.un('mouseleave' ,this.onEventLeave, this);
19505                 el.remove();
19506             },this);
19507         },this);
19508         
19509         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19510             e.remove();
19511         });
19512         
19513     },
19514     
19515     renderEvents: function()
19516     {   
19517         var _this = this;
19518         
19519         this.cells.each(function(c) {
19520             
19521             if(c.row < 5){
19522                 return;
19523             }
19524             
19525             var ev = c.events;
19526             
19527             var r = 4;
19528             if(c.row != c.events.length){
19529                 r = 4 - (4 - (c.row - c.events.length));
19530             }
19531             
19532             c.events = ev.slice(0, r);
19533             c.more = ev.slice(r);
19534             
19535             if(c.more.length && c.more.length == 1){
19536                 c.events.push(c.more.pop());
19537             }
19538             
19539             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19540             
19541         });
19542             
19543         this.cells.each(function(c) {
19544             
19545             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19546             
19547             
19548             for (var e = 0; e < c.events.length; e++){
19549                 var ev = c.events[e];
19550                 var rows = ev.rows;
19551                 
19552                 for(var i = 0; i < rows.length; i++) {
19553                 
19554                     // how many rows should it span..
19555
19556                     var  cfg = {
19557                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19558                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19559
19560                         unselectable : "on",
19561                         cn : [
19562                             {
19563                                 cls: 'fc-event-inner',
19564                                 cn : [
19565     //                                {
19566     //                                  tag:'span',
19567     //                                  cls: 'fc-event-time',
19568     //                                  html : cells.length > 1 ? '' : ev.time
19569     //                                },
19570                                     {
19571                                       tag:'span',
19572                                       cls: 'fc-event-title',
19573                                       html : String.format('{0}', ev.title)
19574                                     }
19575
19576
19577                                 ]
19578                             },
19579                             {
19580                                 cls: 'ui-resizable-handle ui-resizable-e',
19581                                 html : '&nbsp;&nbsp;&nbsp'
19582                             }
19583
19584                         ]
19585                     };
19586
19587                     if (i == 0) {
19588                         cfg.cls += ' fc-event-start';
19589                     }
19590                     if ((i+1) == rows.length) {
19591                         cfg.cls += ' fc-event-end';
19592                     }
19593
19594                     var ctr = _this.el.select('.fc-event-container',true).first();
19595                     var cg = ctr.createChild(cfg);
19596
19597                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19598                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19599
19600                     var r = (c.more.length) ? 1 : 0;
19601                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19602                     cg.setWidth(ebox.right - sbox.x -2);
19603
19604                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19605                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19606                     cg.on('click', _this.onEventClick, _this, ev);
19607
19608                     ev.els.push(cg);
19609                     
19610                 }
19611                 
19612             }
19613             
19614             
19615             if(c.more.length){
19616                 var  cfg = {
19617                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19618                     style : 'position: absolute',
19619                     unselectable : "on",
19620                     cn : [
19621                         {
19622                             cls: 'fc-event-inner',
19623                             cn : [
19624                                 {
19625                                   tag:'span',
19626                                   cls: 'fc-event-title',
19627                                   html : 'More'
19628                                 }
19629
19630
19631                             ]
19632                         },
19633                         {
19634                             cls: 'ui-resizable-handle ui-resizable-e',
19635                             html : '&nbsp;&nbsp;&nbsp'
19636                         }
19637
19638                     ]
19639                 };
19640
19641                 var ctr = _this.el.select('.fc-event-container',true).first();
19642                 var cg = ctr.createChild(cfg);
19643
19644                 var sbox = c.select('.fc-day-content',true).first().getBox();
19645                 var ebox = c.select('.fc-day-content',true).first().getBox();
19646                 //Roo.log(cg);
19647                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19648                 cg.setWidth(ebox.right - sbox.x -2);
19649
19650                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19651                 
19652             }
19653             
19654         });
19655         
19656         
19657         
19658     },
19659     
19660     onEventEnter: function (e, el,event,d) {
19661         this.fireEvent('evententer', this, el, event);
19662     },
19663     
19664     onEventLeave: function (e, el,event,d) {
19665         this.fireEvent('eventleave', this, el, event);
19666     },
19667     
19668     onEventClick: function (e, el,event,d) {
19669         this.fireEvent('eventclick', this, el, event);
19670     },
19671     
19672     onMonthChange: function () {
19673         this.store.load();
19674     },
19675     
19676     onMoreEventClick: function(e, el, more)
19677     {
19678         var _this = this;
19679         
19680         this.calpopover.placement = 'right';
19681         this.calpopover.setTitle('More');
19682         
19683         this.calpopover.setContent('');
19684         
19685         var ctr = this.calpopover.el.select('.popover-content', true).first();
19686         
19687         Roo.each(more, function(m){
19688             var cfg = {
19689                 cls : 'fc-event-hori fc-event-draggable',
19690                 html : m.title
19691             };
19692             var cg = ctr.createChild(cfg);
19693             
19694             cg.on('click', _this.onEventClick, _this, m);
19695         });
19696         
19697         this.calpopover.show(el);
19698         
19699         
19700     },
19701     
19702     onLoad: function () 
19703     {   
19704         this.calevents = [];
19705         var cal = this;
19706         
19707         if(this.store.getCount() > 0){
19708             this.store.data.each(function(d){
19709                cal.addItem({
19710                     id : d.data.id,
19711                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19712                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19713                     time : d.data.start_time,
19714                     title : d.data.title,
19715                     description : d.data.description,
19716                     venue : d.data.venue
19717                 });
19718             });
19719         }
19720         
19721         this.renderEvents();
19722         
19723         if(this.calevents.length && this.loadMask){
19724             this.maskEl.hide();
19725         }
19726     },
19727     
19728     onBeforeLoad: function()
19729     {
19730         this.clearEvents();
19731         if(this.loadMask){
19732             this.maskEl.show();
19733         }
19734     }
19735 });
19736
19737  
19738  /*
19739  * - LGPL
19740  *
19741  * element
19742  * 
19743  */
19744
19745 /**
19746  * @class Roo.bootstrap.Popover
19747  * @extends Roo.bootstrap.Component
19748  * Bootstrap Popover class
19749  * @cfg {String} html contents of the popover   (or false to use children..)
19750  * @cfg {String} title of popover (or false to hide)
19751  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19752  * @cfg {String} trigger click || hover (or false to trigger manually)
19753  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19754  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19755  *      - if false and it has a 'parent' then it will be automatically added to that element
19756  *      - if string - Roo.get  will be called 
19757  * @cfg {Number} delay - delay before showing
19758  
19759  * @constructor
19760  * Create a new Popover
19761  * @param {Object} config The config object
19762  */
19763
19764 Roo.bootstrap.Popover = function(config){
19765     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19766     
19767     this.addEvents({
19768         // raw events
19769          /**
19770          * @event show
19771          * After the popover show
19772          * 
19773          * @param {Roo.bootstrap.Popover} this
19774          */
19775         "show" : true,
19776         /**
19777          * @event hide
19778          * After the popover hide
19779          * 
19780          * @param {Roo.bootstrap.Popover} this
19781          */
19782         "hide" : true
19783     });
19784 };
19785
19786 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19787     
19788     title: false,
19789     html: false,
19790     
19791     placement : 'right',
19792     trigger : 'hover', // hover
19793     modal : false,
19794     delay : 0,
19795     
19796     over: false,
19797     
19798     can_build_overlaid : false,
19799     
19800     maskEl : false, // the mask element
19801     headerEl : false,
19802     contentEl : false,
19803     alignEl : false, // when show is called with an element - this get's stored.
19804     
19805     getChildContainer : function()
19806     {
19807         return this.contentEl;
19808         
19809     },
19810     getPopoverHeader : function()
19811     {
19812         this.title = true; // flag not to hide it..
19813         this.headerEl.addClass('p-0');
19814         return this.headerEl
19815     },
19816     
19817     
19818     getAutoCreate : function(){
19819          
19820         var cfg = {
19821            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19822            style: 'display:block',
19823            cn : [
19824                 {
19825                     cls : 'arrow'
19826                 },
19827                 {
19828                     cls : 'popover-inner ',
19829                     cn : [
19830                         {
19831                             tag: 'h3',
19832                             cls: 'popover-title popover-header',
19833                             html : this.title === false ? '' : this.title
19834                         },
19835                         {
19836                             cls : 'popover-content popover-body '  + (this.cls || ''),
19837                             html : this.html || ''
19838                         }
19839                     ]
19840                     
19841                 }
19842            ]
19843         };
19844         
19845         return cfg;
19846     },
19847     /**
19848      * @param {string} the title
19849      */
19850     setTitle: function(str)
19851     {
19852         this.title = str;
19853         if (this.el) {
19854             this.headerEl.dom.innerHTML = str;
19855         }
19856         
19857     },
19858     /**
19859      * @param {string} the body content
19860      */
19861     setContent: function(str)
19862     {
19863         this.html = str;
19864         if (this.contentEl) {
19865             this.contentEl.dom.innerHTML = str;
19866         }
19867         
19868     },
19869     // as it get's added to the bottom of the page.
19870     onRender : function(ct, position)
19871     {
19872         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19873         
19874         
19875         
19876         if(!this.el){
19877             var cfg = Roo.apply({},  this.getAutoCreate());
19878             cfg.id = Roo.id();
19879             
19880             if (this.cls) {
19881                 cfg.cls += ' ' + this.cls;
19882             }
19883             if (this.style) {
19884                 cfg.style = this.style;
19885             }
19886             //Roo.log("adding to ");
19887             this.el = Roo.get(document.body).createChild(cfg, position);
19888 //            Roo.log(this.el);
19889         }
19890         
19891         this.contentEl = this.el.select('.popover-content',true).first();
19892         this.headerEl =  this.el.select('.popover-title',true).first();
19893         
19894         var nitems = [];
19895         if(typeof(this.items) != 'undefined'){
19896             var items = this.items;
19897             delete this.items;
19898
19899             for(var i =0;i < items.length;i++) {
19900                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19901             }
19902         }
19903
19904         this.items = nitems;
19905         
19906         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19907         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19908         
19909         
19910         
19911         this.initEvents();
19912     },
19913     
19914     resizeMask : function()
19915     {
19916         this.maskEl.setSize(
19917             Roo.lib.Dom.getViewWidth(true),
19918             Roo.lib.Dom.getViewHeight(true)
19919         );
19920     },
19921     
19922     initEvents : function()
19923     {
19924         
19925         if (!this.modal) { 
19926             Roo.bootstrap.Popover.register(this);
19927         }
19928          
19929         this.arrowEl = this.el.select('.arrow',true).first();
19930         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19931         this.el.enableDisplayMode('block');
19932         this.el.hide();
19933  
19934         
19935         if (this.over === false && !this.parent()) {
19936             return; 
19937         }
19938         if (this.triggers === false) {
19939             return;
19940         }
19941          
19942         // support parent
19943         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19944         var triggers = this.trigger ? this.trigger.split(' ') : [];
19945         Roo.each(triggers, function(trigger) {
19946         
19947             if (trigger == 'click') {
19948                 on_el.on('click', this.toggle, this);
19949             } else if (trigger != 'manual') {
19950                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19951                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19952       
19953                 on_el.on(eventIn  ,this.enter, this);
19954                 on_el.on(eventOut, this.leave, this);
19955             }
19956         }, this);
19957     },
19958     
19959     
19960     // private
19961     timeout : null,
19962     hoverState : null,
19963     
19964     toggle : function () {
19965         this.hoverState == 'in' ? this.leave() : this.enter();
19966     },
19967     
19968     enter : function () {
19969         
19970         clearTimeout(this.timeout);
19971     
19972         this.hoverState = 'in';
19973     
19974         if (!this.delay || !this.delay.show) {
19975             this.show();
19976             return;
19977         }
19978         var _t = this;
19979         this.timeout = setTimeout(function () {
19980             if (_t.hoverState == 'in') {
19981                 _t.show();
19982             }
19983         }, this.delay.show)
19984     },
19985     
19986     leave : function() {
19987         clearTimeout(this.timeout);
19988     
19989         this.hoverState = 'out';
19990     
19991         if (!this.delay || !this.delay.hide) {
19992             this.hide();
19993             return;
19994         }
19995         var _t = this;
19996         this.timeout = setTimeout(function () {
19997             if (_t.hoverState == 'out') {
19998                 _t.hide();
19999             }
20000         }, this.delay.hide)
20001     },
20002     /**
20003      * Show the popover
20004      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20005      * @param {string} (left|right|top|bottom) position
20006      */
20007     show : function (on_el, placement)
20008     {
20009         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
20010         on_el = on_el || false; // default to false
20011          
20012         if (!on_el) {
20013             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20014                 on_el = this.parent().el;
20015             } else if (this.over) {
20016                 Roo.get(this.over);
20017             }
20018             
20019         }
20020         
20021         this.alignEl = Roo.get( on_el );
20022
20023         if (!this.el) {
20024             this.render(document.body);
20025         }
20026         
20027         
20028          
20029         
20030         if (this.title === false) {
20031             this.headerEl.hide();
20032         }
20033         
20034        
20035         this.el.show();
20036         this.el.dom.style.display = 'block';
20037          
20038  
20039         if (this.alignEl) {
20040             this.updatePosition(this.placement, true);
20041              
20042         } else {
20043             // this is usually just done by the builder = to show the popoup in the middle of the scren.
20044             var es = this.el.getSize();
20045             var x = Roo.lib.Dom.getViewWidth()/2;
20046             var y = Roo.lib.Dom.getViewHeight()/2;
20047             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20048             
20049         }
20050
20051         
20052         //var arrow = this.el.select('.arrow',true).first();
20053         //arrow.set(align[2], 
20054         
20055         this.el.addClass('in');
20056         
20057          
20058         
20059         this.hoverState = 'in';
20060         
20061         if (this.modal) {
20062             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20063             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20064             this.maskEl.dom.style.display = 'block';
20065             this.maskEl.addClass('show');
20066         }
20067         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20068  
20069         this.fireEvent('show', this);
20070         
20071     },
20072     /**
20073      * fire this manually after loading a grid in the table for example
20074      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20075      * @param {Boolean} try and move it if we cant get right position.
20076      */
20077     updatePosition : function(placement, try_move)
20078     {
20079         // allow for calling with no parameters
20080         placement = placement   ? placement :  this.placement;
20081         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20082         
20083         this.el.removeClass([
20084             'fade','top','bottom', 'left', 'right','in',
20085             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20086         ]);
20087         this.el.addClass(placement + ' bs-popover-' + placement);
20088         
20089         if (!this.alignEl ) {
20090             return false;
20091         }
20092         
20093         switch (placement) {
20094             case 'right':
20095                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20096                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20097                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20098                     //normal display... or moved up/down.
20099                     this.el.setXY(offset);
20100                     var xy = this.alignEl.getAnchorXY('tr', false);
20101                     xy[0]+=2;xy[1]+=5;
20102                     this.arrowEl.setXY(xy);
20103                     return true;
20104                 }
20105                 // continue through...
20106                 return this.updatePosition('left', false);
20107                 
20108             
20109             case 'left':
20110                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20111                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20112                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20113                     //normal display... or moved up/down.
20114                     this.el.setXY(offset);
20115                     var xy = this.alignEl.getAnchorXY('tl', false);
20116                     xy[0]-=10;xy[1]+=5; // << fix me
20117                     this.arrowEl.setXY(xy);
20118                     return true;
20119                 }
20120                 // call self...
20121                 return this.updatePosition('right', false);
20122             
20123             case 'top':
20124                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20125                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20126                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20127                     //normal display... or moved up/down.
20128                     this.el.setXY(offset);
20129                     var xy = this.alignEl.getAnchorXY('t', false);
20130                     xy[1]-=10; // << fix me
20131                     this.arrowEl.setXY(xy);
20132                     return true;
20133                 }
20134                 // fall through
20135                return this.updatePosition('bottom', false);
20136             
20137             case 'bottom':
20138                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20139                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20140                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20141                     //normal display... or moved up/down.
20142                     this.el.setXY(offset);
20143                     var xy = this.alignEl.getAnchorXY('b', false);
20144                      xy[1]+=2; // << fix me
20145                     this.arrowEl.setXY(xy);
20146                     return true;
20147                 }
20148                 // fall through
20149                 return this.updatePosition('top', false);
20150                 
20151             
20152         }
20153         
20154         
20155         return false;
20156     },
20157     
20158     hide : function()
20159     {
20160         this.el.setXY([0,0]);
20161         this.el.removeClass('in');
20162         this.el.hide();
20163         this.hoverState = null;
20164         this.maskEl.hide(); // always..
20165         this.fireEvent('hide', this);
20166     }
20167     
20168 });
20169
20170
20171 Roo.apply(Roo.bootstrap.Popover, {
20172
20173     alignment : {
20174         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20175         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20176         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20177         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20178     },
20179     
20180     zIndex : 20001,
20181
20182     clickHander : false,
20183     
20184
20185     onMouseDown : function(e)
20186     {
20187         if (!e.getTarget(".roo-popover")) {
20188             this.hideAll();
20189         }
20190          
20191     },
20192     
20193     popups : [],
20194     
20195     register : function(popup)
20196     {
20197         if (!Roo.bootstrap.Popover.clickHandler) {
20198             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20199         }
20200         // hide other popups.
20201         this.hideAll();
20202         this.popups.push(popup);
20203     },
20204     hideAll : function()
20205     {
20206         this.popups.forEach(function(p) {
20207             p.hide();
20208         });
20209     }
20210
20211 });/*
20212  * - LGPL
20213  *
20214  * Card header - holder for the card header elements.
20215  * 
20216  */
20217
20218 /**
20219  * @class Roo.bootstrap.PopoverNav
20220  * @extends Roo.bootstrap.NavGroup
20221  * Bootstrap Popover header navigation class
20222  * @constructor
20223  * Create a new Popover Header Navigation 
20224  * @param {Object} config The config object
20225  */
20226
20227 Roo.bootstrap.PopoverNav = function(config){
20228     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20229 };
20230
20231 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20232     
20233     
20234     container_method : 'getPopoverHeader' 
20235     
20236      
20237     
20238     
20239    
20240 });
20241
20242  
20243
20244  /*
20245  * - LGPL
20246  *
20247  * Progress
20248  * 
20249  */
20250
20251 /**
20252  * @class Roo.bootstrap.Progress
20253  * @extends Roo.bootstrap.Component
20254  * Bootstrap Progress class
20255  * @cfg {Boolean} striped striped of the progress bar
20256  * @cfg {Boolean} active animated of the progress bar
20257  * 
20258  * 
20259  * @constructor
20260  * Create a new Progress
20261  * @param {Object} config The config object
20262  */
20263
20264 Roo.bootstrap.Progress = function(config){
20265     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20266 };
20267
20268 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20269     
20270     striped : false,
20271     active: false,
20272     
20273     getAutoCreate : function(){
20274         var cfg = {
20275             tag: 'div',
20276             cls: 'progress'
20277         };
20278         
20279         
20280         if(this.striped){
20281             cfg.cls += ' progress-striped';
20282         }
20283       
20284         if(this.active){
20285             cfg.cls += ' active';
20286         }
20287         
20288         
20289         return cfg;
20290     }
20291    
20292 });
20293
20294  
20295
20296  /*
20297  * - LGPL
20298  *
20299  * ProgressBar
20300  * 
20301  */
20302
20303 /**
20304  * @class Roo.bootstrap.ProgressBar
20305  * @extends Roo.bootstrap.Component
20306  * Bootstrap ProgressBar class
20307  * @cfg {Number} aria_valuenow aria-value now
20308  * @cfg {Number} aria_valuemin aria-value min
20309  * @cfg {Number} aria_valuemax aria-value max
20310  * @cfg {String} label label for the progress bar
20311  * @cfg {String} panel (success | info | warning | danger )
20312  * @cfg {String} role role of the progress bar
20313  * @cfg {String} sr_only text
20314  * 
20315  * 
20316  * @constructor
20317  * Create a new ProgressBar
20318  * @param {Object} config The config object
20319  */
20320
20321 Roo.bootstrap.ProgressBar = function(config){
20322     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20323 };
20324
20325 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20326     
20327     aria_valuenow : 0,
20328     aria_valuemin : 0,
20329     aria_valuemax : 100,
20330     label : false,
20331     panel : false,
20332     role : false,
20333     sr_only: false,
20334     
20335     getAutoCreate : function()
20336     {
20337         
20338         var cfg = {
20339             tag: 'div',
20340             cls: 'progress-bar',
20341             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20342         };
20343         
20344         if(this.sr_only){
20345             cfg.cn = {
20346                 tag: 'span',
20347                 cls: 'sr-only',
20348                 html: this.sr_only
20349             }
20350         }
20351         
20352         if(this.role){
20353             cfg.role = this.role;
20354         }
20355         
20356         if(this.aria_valuenow){
20357             cfg['aria-valuenow'] = this.aria_valuenow;
20358         }
20359         
20360         if(this.aria_valuemin){
20361             cfg['aria-valuemin'] = this.aria_valuemin;
20362         }
20363         
20364         if(this.aria_valuemax){
20365             cfg['aria-valuemax'] = this.aria_valuemax;
20366         }
20367         
20368         if(this.label && !this.sr_only){
20369             cfg.html = this.label;
20370         }
20371         
20372         if(this.panel){
20373             cfg.cls += ' progress-bar-' + this.panel;
20374         }
20375         
20376         return cfg;
20377     },
20378     
20379     update : function(aria_valuenow)
20380     {
20381         this.aria_valuenow = aria_valuenow;
20382         
20383         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20384     }
20385    
20386 });
20387
20388  
20389
20390  /*
20391  * - LGPL
20392  *
20393  * column
20394  * 
20395  */
20396
20397 /**
20398  * @class Roo.bootstrap.TabGroup
20399  * @extends Roo.bootstrap.Column
20400  * Bootstrap Column class
20401  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20402  * @cfg {Boolean} carousel true to make the group behave like a carousel
20403  * @cfg {Boolean} bullets show bullets for the panels
20404  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20405  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20406  * @cfg {Boolean} showarrow (true|false) show arrow default true
20407  * 
20408  * @constructor
20409  * Create a new TabGroup
20410  * @param {Object} config The config object
20411  */
20412
20413 Roo.bootstrap.TabGroup = function(config){
20414     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20415     if (!this.navId) {
20416         this.navId = Roo.id();
20417     }
20418     this.tabs = [];
20419     Roo.bootstrap.TabGroup.register(this);
20420     
20421 };
20422
20423 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20424     
20425     carousel : false,
20426     transition : false,
20427     bullets : 0,
20428     timer : 0,
20429     autoslide : false,
20430     slideFn : false,
20431     slideOnTouch : false,
20432     showarrow : true,
20433     
20434     getAutoCreate : function()
20435     {
20436         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20437         
20438         cfg.cls += ' tab-content';
20439         
20440         if (this.carousel) {
20441             cfg.cls += ' carousel slide';
20442             
20443             cfg.cn = [{
20444                cls : 'carousel-inner',
20445                cn : []
20446             }];
20447         
20448             if(this.bullets  && !Roo.isTouch){
20449                 
20450                 var bullets = {
20451                     cls : 'carousel-bullets',
20452                     cn : []
20453                 };
20454                
20455                 if(this.bullets_cls){
20456                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20457                 }
20458                 
20459                 bullets.cn.push({
20460                     cls : 'clear'
20461                 });
20462                 
20463                 cfg.cn[0].cn.push(bullets);
20464             }
20465             
20466             if(this.showarrow){
20467                 cfg.cn[0].cn.push({
20468                     tag : 'div',
20469                     class : 'carousel-arrow',
20470                     cn : [
20471                         {
20472                             tag : 'div',
20473                             class : 'carousel-prev',
20474                             cn : [
20475                                 {
20476                                     tag : 'i',
20477                                     class : 'fa fa-chevron-left'
20478                                 }
20479                             ]
20480                         },
20481                         {
20482                             tag : 'div',
20483                             class : 'carousel-next',
20484                             cn : [
20485                                 {
20486                                     tag : 'i',
20487                                     class : 'fa fa-chevron-right'
20488                                 }
20489                             ]
20490                         }
20491                     ]
20492                 });
20493             }
20494             
20495         }
20496         
20497         return cfg;
20498     },
20499     
20500     initEvents:  function()
20501     {
20502 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20503 //            this.el.on("touchstart", this.onTouchStart, this);
20504 //        }
20505         
20506         if(this.autoslide){
20507             var _this = this;
20508             
20509             this.slideFn = window.setInterval(function() {
20510                 _this.showPanelNext();
20511             }, this.timer);
20512         }
20513         
20514         if(this.showarrow){
20515             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20516             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20517         }
20518         
20519         
20520     },
20521     
20522 //    onTouchStart : function(e, el, o)
20523 //    {
20524 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20525 //            return;
20526 //        }
20527 //        
20528 //        this.showPanelNext();
20529 //    },
20530     
20531     
20532     getChildContainer : function()
20533     {
20534         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20535     },
20536     
20537     /**
20538     * register a Navigation item
20539     * @param {Roo.bootstrap.NavItem} the navitem to add
20540     */
20541     register : function(item)
20542     {
20543         this.tabs.push( item);
20544         item.navId = this.navId; // not really needed..
20545         this.addBullet();
20546     
20547     },
20548     
20549     getActivePanel : function()
20550     {
20551         var r = false;
20552         Roo.each(this.tabs, function(t) {
20553             if (t.active) {
20554                 r = t;
20555                 return false;
20556             }
20557             return null;
20558         });
20559         return r;
20560         
20561     },
20562     getPanelByName : function(n)
20563     {
20564         var r = false;
20565         Roo.each(this.tabs, function(t) {
20566             if (t.tabId == n) {
20567                 r = t;
20568                 return false;
20569             }
20570             return null;
20571         });
20572         return r;
20573     },
20574     indexOfPanel : function(p)
20575     {
20576         var r = false;
20577         Roo.each(this.tabs, function(t,i) {
20578             if (t.tabId == p.tabId) {
20579                 r = i;
20580                 return false;
20581             }
20582             return null;
20583         });
20584         return r;
20585     },
20586     /**
20587      * show a specific panel
20588      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20589      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20590      */
20591     showPanel : function (pan)
20592     {
20593         if(this.transition || typeof(pan) == 'undefined'){
20594             Roo.log("waiting for the transitionend");
20595             return false;
20596         }
20597         
20598         if (typeof(pan) == 'number') {
20599             pan = this.tabs[pan];
20600         }
20601         
20602         if (typeof(pan) == 'string') {
20603             pan = this.getPanelByName(pan);
20604         }
20605         
20606         var cur = this.getActivePanel();
20607         
20608         if(!pan || !cur){
20609             Roo.log('pan or acitve pan is undefined');
20610             return false;
20611         }
20612         
20613         if (pan.tabId == this.getActivePanel().tabId) {
20614             return true;
20615         }
20616         
20617         if (false === cur.fireEvent('beforedeactivate')) {
20618             return false;
20619         }
20620         
20621         if(this.bullets > 0 && !Roo.isTouch){
20622             this.setActiveBullet(this.indexOfPanel(pan));
20623         }
20624         
20625         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20626             
20627             //class="carousel-item carousel-item-next carousel-item-left"
20628             
20629             this.transition = true;
20630             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20631             var lr = dir == 'next' ? 'left' : 'right';
20632             pan.el.addClass(dir); // or prev
20633             pan.el.addClass('carousel-item-' + dir); // or prev
20634             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20635             cur.el.addClass(lr); // or right
20636             pan.el.addClass(lr);
20637             cur.el.addClass('carousel-item-' +lr); // or right
20638             pan.el.addClass('carousel-item-' +lr);
20639             
20640             
20641             var _this = this;
20642             cur.el.on('transitionend', function() {
20643                 Roo.log("trans end?");
20644                 
20645                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20646                 pan.setActive(true);
20647                 
20648                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20649                 cur.setActive(false);
20650                 
20651                 _this.transition = false;
20652                 
20653             }, this, { single:  true } );
20654             
20655             return true;
20656         }
20657         
20658         cur.setActive(false);
20659         pan.setActive(true);
20660         
20661         return true;
20662         
20663     },
20664     showPanelNext : function()
20665     {
20666         var i = this.indexOfPanel(this.getActivePanel());
20667         
20668         if (i >= this.tabs.length - 1 && !this.autoslide) {
20669             return;
20670         }
20671         
20672         if (i >= this.tabs.length - 1 && this.autoslide) {
20673             i = -1;
20674         }
20675         
20676         this.showPanel(this.tabs[i+1]);
20677     },
20678     
20679     showPanelPrev : function()
20680     {
20681         var i = this.indexOfPanel(this.getActivePanel());
20682         
20683         if (i  < 1 && !this.autoslide) {
20684             return;
20685         }
20686         
20687         if (i < 1 && this.autoslide) {
20688             i = this.tabs.length;
20689         }
20690         
20691         this.showPanel(this.tabs[i-1]);
20692     },
20693     
20694     
20695     addBullet: function()
20696     {
20697         if(!this.bullets || Roo.isTouch){
20698             return;
20699         }
20700         var ctr = this.el.select('.carousel-bullets',true).first();
20701         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20702         var bullet = ctr.createChild({
20703             cls : 'bullet bullet-' + i
20704         },ctr.dom.lastChild);
20705         
20706         
20707         var _this = this;
20708         
20709         bullet.on('click', (function(e, el, o, ii, t){
20710
20711             e.preventDefault();
20712
20713             this.showPanel(ii);
20714
20715             if(this.autoslide && this.slideFn){
20716                 clearInterval(this.slideFn);
20717                 this.slideFn = window.setInterval(function() {
20718                     _this.showPanelNext();
20719                 }, this.timer);
20720             }
20721
20722         }).createDelegate(this, [i, bullet], true));
20723                 
20724         
20725     },
20726      
20727     setActiveBullet : function(i)
20728     {
20729         if(Roo.isTouch){
20730             return;
20731         }
20732         
20733         Roo.each(this.el.select('.bullet', true).elements, function(el){
20734             el.removeClass('selected');
20735         });
20736
20737         var bullet = this.el.select('.bullet-' + i, true).first();
20738         
20739         if(!bullet){
20740             return;
20741         }
20742         
20743         bullet.addClass('selected');
20744     }
20745     
20746     
20747   
20748 });
20749
20750  
20751
20752  
20753  
20754 Roo.apply(Roo.bootstrap.TabGroup, {
20755     
20756     groups: {},
20757      /**
20758     * register a Navigation Group
20759     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20760     */
20761     register : function(navgrp)
20762     {
20763         this.groups[navgrp.navId] = navgrp;
20764         
20765     },
20766     /**
20767     * fetch a Navigation Group based on the navigation ID
20768     * if one does not exist , it will get created.
20769     * @param {string} the navgroup to add
20770     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20771     */
20772     get: function(navId) {
20773         if (typeof(this.groups[navId]) == 'undefined') {
20774             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20775         }
20776         return this.groups[navId] ;
20777     }
20778     
20779     
20780     
20781 });
20782
20783  /*
20784  * - LGPL
20785  *
20786  * TabPanel
20787  * 
20788  */
20789
20790 /**
20791  * @class Roo.bootstrap.TabPanel
20792  * @extends Roo.bootstrap.Component
20793  * Bootstrap TabPanel class
20794  * @cfg {Boolean} active panel active
20795  * @cfg {String} html panel content
20796  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20797  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20798  * @cfg {String} href click to link..
20799  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20800  * 
20801  * 
20802  * @constructor
20803  * Create a new TabPanel
20804  * @param {Object} config The config object
20805  */
20806
20807 Roo.bootstrap.TabPanel = function(config){
20808     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20809     this.addEvents({
20810         /**
20811              * @event changed
20812              * Fires when the active status changes
20813              * @param {Roo.bootstrap.TabPanel} this
20814              * @param {Boolean} state the new state
20815             
20816          */
20817         'changed': true,
20818         /**
20819              * @event beforedeactivate
20820              * Fires before a tab is de-activated - can be used to do validation on a form.
20821              * @param {Roo.bootstrap.TabPanel} this
20822              * @return {Boolean} false if there is an error
20823             
20824          */
20825         'beforedeactivate': true
20826      });
20827     
20828     this.tabId = this.tabId || Roo.id();
20829   
20830 };
20831
20832 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20833     
20834     active: false,
20835     html: false,
20836     tabId: false,
20837     navId : false,
20838     href : '',
20839     touchSlide : false,
20840     getAutoCreate : function(){
20841         
20842         
20843         var cfg = {
20844             tag: 'div',
20845             // item is needed for carousel - not sure if it has any effect otherwise
20846             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20847             html: this.html || ''
20848         };
20849         
20850         if(this.active){
20851             cfg.cls += ' active';
20852         }
20853         
20854         if(this.tabId){
20855             cfg.tabId = this.tabId;
20856         }
20857         
20858         
20859         
20860         return cfg;
20861     },
20862     
20863     initEvents:  function()
20864     {
20865         var p = this.parent();
20866         
20867         this.navId = this.navId || p.navId;
20868         
20869         if (typeof(this.navId) != 'undefined') {
20870             // not really needed.. but just in case.. parent should be a NavGroup.
20871             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20872             
20873             tg.register(this);
20874             
20875             var i = tg.tabs.length - 1;
20876             
20877             if(this.active && tg.bullets > 0 && i < tg.bullets){
20878                 tg.setActiveBullet(i);
20879             }
20880         }
20881         
20882         this.el.on('click', this.onClick, this);
20883         
20884         if(Roo.isTouch && this.touchSlide){
20885             this.el.on("touchstart", this.onTouchStart, this);
20886             this.el.on("touchmove", this.onTouchMove, this);
20887             this.el.on("touchend", this.onTouchEnd, this);
20888         }
20889         
20890     },
20891     
20892     onRender : function(ct, position)
20893     {
20894         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20895     },
20896     
20897     setActive : function(state)
20898     {
20899         Roo.log("panel - set active " + this.tabId + "=" + state);
20900         
20901         this.active = state;
20902         if (!state) {
20903             this.el.removeClass('active');
20904             
20905         } else  if (!this.el.hasClass('active')) {
20906             this.el.addClass('active');
20907         }
20908         
20909         this.fireEvent('changed', this, state);
20910     },
20911     
20912     onClick : function(e)
20913     {
20914         e.preventDefault();
20915         
20916         if(!this.href.length){
20917             return;
20918         }
20919         
20920         window.location.href = this.href;
20921     },
20922     
20923     startX : 0,
20924     startY : 0,
20925     endX : 0,
20926     endY : 0,
20927     swiping : false,
20928     
20929     onTouchStart : function(e)
20930     {
20931         this.swiping = false;
20932         
20933         this.startX = e.browserEvent.touches[0].clientX;
20934         this.startY = e.browserEvent.touches[0].clientY;
20935     },
20936     
20937     onTouchMove : function(e)
20938     {
20939         this.swiping = true;
20940         
20941         this.endX = e.browserEvent.touches[0].clientX;
20942         this.endY = e.browserEvent.touches[0].clientY;
20943     },
20944     
20945     onTouchEnd : function(e)
20946     {
20947         if(!this.swiping){
20948             this.onClick(e);
20949             return;
20950         }
20951         
20952         var tabGroup = this.parent();
20953         
20954         if(this.endX > this.startX){ // swiping right
20955             tabGroup.showPanelPrev();
20956             return;
20957         }
20958         
20959         if(this.startX > this.endX){ // swiping left
20960             tabGroup.showPanelNext();
20961             return;
20962         }
20963     }
20964     
20965     
20966 });
20967  
20968
20969  
20970
20971  /*
20972  * - LGPL
20973  *
20974  * DateField
20975  * 
20976  */
20977
20978 /**
20979  * @class Roo.bootstrap.DateField
20980  * @extends Roo.bootstrap.Input
20981  * Bootstrap DateField class
20982  * @cfg {Number} weekStart default 0
20983  * @cfg {String} viewMode default empty, (months|years)
20984  * @cfg {String} minViewMode default empty, (months|years)
20985  * @cfg {Number} startDate default -Infinity
20986  * @cfg {Number} endDate default Infinity
20987  * @cfg {Boolean} todayHighlight default false
20988  * @cfg {Boolean} todayBtn default false
20989  * @cfg {Boolean} calendarWeeks default false
20990  * @cfg {Object} daysOfWeekDisabled default empty
20991  * @cfg {Boolean} singleMode default false (true | false)
20992  * 
20993  * @cfg {Boolean} keyboardNavigation default true
20994  * @cfg {String} language default en
20995  * 
20996  * @constructor
20997  * Create a new DateField
20998  * @param {Object} config The config object
20999  */
21000
21001 Roo.bootstrap.DateField = function(config){
21002     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21003      this.addEvents({
21004             /**
21005              * @event show
21006              * Fires when this field show.
21007              * @param {Roo.bootstrap.DateField} this
21008              * @param {Mixed} date The date value
21009              */
21010             show : true,
21011             /**
21012              * @event show
21013              * Fires when this field hide.
21014              * @param {Roo.bootstrap.DateField} this
21015              * @param {Mixed} date The date value
21016              */
21017             hide : true,
21018             /**
21019              * @event select
21020              * Fires when select a date.
21021              * @param {Roo.bootstrap.DateField} this
21022              * @param {Mixed} date The date value
21023              */
21024             select : true,
21025             /**
21026              * @event beforeselect
21027              * Fires when before select a date.
21028              * @param {Roo.bootstrap.DateField} this
21029              * @param {Mixed} date The date value
21030              */
21031             beforeselect : true
21032         });
21033 };
21034
21035 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
21036     
21037     /**
21038      * @cfg {String} format
21039      * The default date format string which can be overriden for localization support.  The format must be
21040      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21041      */
21042     format : "m/d/y",
21043     /**
21044      * @cfg {String} altFormats
21045      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21046      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21047      */
21048     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21049     
21050     weekStart : 0,
21051     
21052     viewMode : '',
21053     
21054     minViewMode : '',
21055     
21056     todayHighlight : false,
21057     
21058     todayBtn: false,
21059     
21060     language: 'en',
21061     
21062     keyboardNavigation: true,
21063     
21064     calendarWeeks: false,
21065     
21066     startDate: -Infinity,
21067     
21068     endDate: Infinity,
21069     
21070     daysOfWeekDisabled: [],
21071     
21072     _events: [],
21073     
21074     singleMode : false,
21075     
21076     UTCDate: function()
21077     {
21078         return new Date(Date.UTC.apply(Date, arguments));
21079     },
21080     
21081     UTCToday: function()
21082     {
21083         var today = new Date();
21084         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21085     },
21086     
21087     getDate: function() {
21088             var d = this.getUTCDate();
21089             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21090     },
21091     
21092     getUTCDate: function() {
21093             return this.date;
21094     },
21095     
21096     setDate: function(d) {
21097             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21098     },
21099     
21100     setUTCDate: function(d) {
21101             this.date = d;
21102             this.setValue(this.formatDate(this.date));
21103     },
21104         
21105     onRender: function(ct, position)
21106     {
21107         
21108         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21109         
21110         this.language = this.language || 'en';
21111         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21112         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21113         
21114         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21115         this.format = this.format || 'm/d/y';
21116         this.isInline = false;
21117         this.isInput = true;
21118         this.component = this.el.select('.add-on', true).first() || false;
21119         this.component = (this.component && this.component.length === 0) ? false : this.component;
21120         this.hasInput = this.component && this.inputEl().length;
21121         
21122         if (typeof(this.minViewMode === 'string')) {
21123             switch (this.minViewMode) {
21124                 case 'months':
21125                     this.minViewMode = 1;
21126                     break;
21127                 case 'years':
21128                     this.minViewMode = 2;
21129                     break;
21130                 default:
21131                     this.minViewMode = 0;
21132                     break;
21133             }
21134         }
21135         
21136         if (typeof(this.viewMode === 'string')) {
21137             switch (this.viewMode) {
21138                 case 'months':
21139                     this.viewMode = 1;
21140                     break;
21141                 case 'years':
21142                     this.viewMode = 2;
21143                     break;
21144                 default:
21145                     this.viewMode = 0;
21146                     break;
21147             }
21148         }
21149                 
21150         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21151         
21152 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21153         
21154         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21155         
21156         this.picker().on('mousedown', this.onMousedown, this);
21157         this.picker().on('click', this.onClick, this);
21158         
21159         this.picker().addClass('datepicker-dropdown');
21160         
21161         this.startViewMode = this.viewMode;
21162         
21163         if(this.singleMode){
21164             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21165                 v.setVisibilityMode(Roo.Element.DISPLAY);
21166                 v.hide();
21167             });
21168             
21169             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21170                 v.setStyle('width', '189px');
21171             });
21172         }
21173         
21174         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21175             if(!this.calendarWeeks){
21176                 v.remove();
21177                 return;
21178             }
21179             
21180             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21181             v.attr('colspan', function(i, val){
21182                 return parseInt(val) + 1;
21183             });
21184         });
21185                         
21186         
21187         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21188         
21189         this.setStartDate(this.startDate);
21190         this.setEndDate(this.endDate);
21191         
21192         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21193         
21194         this.fillDow();
21195         this.fillMonths();
21196         this.update();
21197         this.showMode();
21198         
21199         if(this.isInline) {
21200             this.showPopup();
21201         }
21202     },
21203     
21204     picker : function()
21205     {
21206         return this.pickerEl;
21207 //        return this.el.select('.datepicker', true).first();
21208     },
21209     
21210     fillDow: function()
21211     {
21212         var dowCnt = this.weekStart;
21213         
21214         var dow = {
21215             tag: 'tr',
21216             cn: [
21217                 
21218             ]
21219         };
21220         
21221         if(this.calendarWeeks){
21222             dow.cn.push({
21223                 tag: 'th',
21224                 cls: 'cw',
21225                 html: '&nbsp;'
21226             })
21227         }
21228         
21229         while (dowCnt < this.weekStart + 7) {
21230             dow.cn.push({
21231                 tag: 'th',
21232                 cls: 'dow',
21233                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21234             });
21235         }
21236         
21237         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21238     },
21239     
21240     fillMonths: function()
21241     {    
21242         var i = 0;
21243         var months = this.picker().select('>.datepicker-months td', true).first();
21244         
21245         months.dom.innerHTML = '';
21246         
21247         while (i < 12) {
21248             var month = {
21249                 tag: 'span',
21250                 cls: 'month',
21251                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21252             };
21253             
21254             months.createChild(month);
21255         }
21256         
21257     },
21258     
21259     update: function()
21260     {
21261         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;
21262         
21263         if (this.date < this.startDate) {
21264             this.viewDate = new Date(this.startDate);
21265         } else if (this.date > this.endDate) {
21266             this.viewDate = new Date(this.endDate);
21267         } else {
21268             this.viewDate = new Date(this.date);
21269         }
21270         
21271         this.fill();
21272     },
21273     
21274     fill: function() 
21275     {
21276         var d = new Date(this.viewDate),
21277                 year = d.getUTCFullYear(),
21278                 month = d.getUTCMonth(),
21279                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21280                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21281                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21282                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21283                 currentDate = this.date && this.date.valueOf(),
21284                 today = this.UTCToday();
21285         
21286         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21287         
21288 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21289         
21290 //        this.picker.select('>tfoot th.today').
21291 //                                              .text(dates[this.language].today)
21292 //                                              .toggle(this.todayBtn !== false);
21293     
21294         this.updateNavArrows();
21295         this.fillMonths();
21296                                                 
21297         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21298         
21299         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21300          
21301         prevMonth.setUTCDate(day);
21302         
21303         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21304         
21305         var nextMonth = new Date(prevMonth);
21306         
21307         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21308         
21309         nextMonth = nextMonth.valueOf();
21310         
21311         var fillMonths = false;
21312         
21313         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21314         
21315         while(prevMonth.valueOf() <= nextMonth) {
21316             var clsName = '';
21317             
21318             if (prevMonth.getUTCDay() === this.weekStart) {
21319                 if(fillMonths){
21320                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21321                 }
21322                     
21323                 fillMonths = {
21324                     tag: 'tr',
21325                     cn: []
21326                 };
21327                 
21328                 if(this.calendarWeeks){
21329                     // ISO 8601: First week contains first thursday.
21330                     // ISO also states week starts on Monday, but we can be more abstract here.
21331                     var
21332                     // Start of current week: based on weekstart/current date
21333                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21334                     // Thursday of this week
21335                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21336                     // First Thursday of year, year from thursday
21337                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21338                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21339                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21340                     
21341                     fillMonths.cn.push({
21342                         tag: 'td',
21343                         cls: 'cw',
21344                         html: calWeek
21345                     });
21346                 }
21347             }
21348             
21349             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21350                 clsName += ' old';
21351             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21352                 clsName += ' new';
21353             }
21354             if (this.todayHighlight &&
21355                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21356                 prevMonth.getUTCMonth() == today.getMonth() &&
21357                 prevMonth.getUTCDate() == today.getDate()) {
21358                 clsName += ' today';
21359             }
21360             
21361             if (currentDate && prevMonth.valueOf() === currentDate) {
21362                 clsName += ' active';
21363             }
21364             
21365             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21366                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21367                     clsName += ' disabled';
21368             }
21369             
21370             fillMonths.cn.push({
21371                 tag: 'td',
21372                 cls: 'day ' + clsName,
21373                 html: prevMonth.getDate()
21374             });
21375             
21376             prevMonth.setDate(prevMonth.getDate()+1);
21377         }
21378           
21379         var currentYear = this.date && this.date.getUTCFullYear();
21380         var currentMonth = this.date && this.date.getUTCMonth();
21381         
21382         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21383         
21384         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21385             v.removeClass('active');
21386             
21387             if(currentYear === year && k === currentMonth){
21388                 v.addClass('active');
21389             }
21390             
21391             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21392                 v.addClass('disabled');
21393             }
21394             
21395         });
21396         
21397         
21398         year = parseInt(year/10, 10) * 10;
21399         
21400         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21401         
21402         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21403         
21404         year -= 1;
21405         for (var i = -1; i < 11; i++) {
21406             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21407                 tag: 'span',
21408                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21409                 html: year
21410             });
21411             
21412             year += 1;
21413         }
21414     },
21415     
21416     showMode: function(dir) 
21417     {
21418         if (dir) {
21419             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21420         }
21421         
21422         Roo.each(this.picker().select('>div',true).elements, function(v){
21423             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21424             v.hide();
21425         });
21426         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21427     },
21428     
21429     place: function()
21430     {
21431         if(this.isInline) {
21432             return;
21433         }
21434         
21435         this.picker().removeClass(['bottom', 'top']);
21436         
21437         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21438             /*
21439              * place to the top of element!
21440              *
21441              */
21442             
21443             this.picker().addClass('top');
21444             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21445             
21446             return;
21447         }
21448         
21449         this.picker().addClass('bottom');
21450         
21451         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21452     },
21453     
21454     parseDate : function(value)
21455     {
21456         if(!value || value instanceof Date){
21457             return value;
21458         }
21459         var v = Date.parseDate(value, this.format);
21460         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21461             v = Date.parseDate(value, 'Y-m-d');
21462         }
21463         if(!v && this.altFormats){
21464             if(!this.altFormatsArray){
21465                 this.altFormatsArray = this.altFormats.split("|");
21466             }
21467             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21468                 v = Date.parseDate(value, this.altFormatsArray[i]);
21469             }
21470         }
21471         return v;
21472     },
21473     
21474     formatDate : function(date, fmt)
21475     {   
21476         return (!date || !(date instanceof Date)) ?
21477         date : date.dateFormat(fmt || this.format);
21478     },
21479     
21480     onFocus : function()
21481     {
21482         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21483         this.showPopup();
21484     },
21485     
21486     onBlur : function()
21487     {
21488         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21489         
21490         var d = this.inputEl().getValue();
21491         
21492         this.setValue(d);
21493                 
21494         this.hidePopup();
21495     },
21496     
21497     showPopup : function()
21498     {
21499         this.picker().show();
21500         this.update();
21501         this.place();
21502         
21503         this.fireEvent('showpopup', this, this.date);
21504     },
21505     
21506     hidePopup : function()
21507     {
21508         if(this.isInline) {
21509             return;
21510         }
21511         this.picker().hide();
21512         this.viewMode = this.startViewMode;
21513         this.showMode();
21514         
21515         this.fireEvent('hidepopup', this, this.date);
21516         
21517     },
21518     
21519     onMousedown: function(e)
21520     {
21521         e.stopPropagation();
21522         e.preventDefault();
21523     },
21524     
21525     keyup: function(e)
21526     {
21527         Roo.bootstrap.DateField.superclass.keyup.call(this);
21528         this.update();
21529     },
21530
21531     setValue: function(v)
21532     {
21533         if(this.fireEvent('beforeselect', this, v) !== false){
21534             var d = new Date(this.parseDate(v) ).clearTime();
21535         
21536             if(isNaN(d.getTime())){
21537                 this.date = this.viewDate = '';
21538                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21539                 return;
21540             }
21541
21542             v = this.formatDate(d);
21543
21544             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21545
21546             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21547
21548             this.update();
21549
21550             this.fireEvent('select', this, this.date);
21551         }
21552     },
21553     
21554     getValue: function()
21555     {
21556         return this.formatDate(this.date);
21557     },
21558     
21559     fireKey: function(e)
21560     {
21561         if (!this.picker().isVisible()){
21562             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21563                 this.showPopup();
21564             }
21565             return;
21566         }
21567         
21568         var dateChanged = false,
21569         dir, day, month,
21570         newDate, newViewDate;
21571         
21572         switch(e.keyCode){
21573             case 27: // escape
21574                 this.hidePopup();
21575                 e.preventDefault();
21576                 break;
21577             case 37: // left
21578             case 39: // right
21579                 if (!this.keyboardNavigation) {
21580                     break;
21581                 }
21582                 dir = e.keyCode == 37 ? -1 : 1;
21583                 
21584                 if (e.ctrlKey){
21585                     newDate = this.moveYear(this.date, dir);
21586                     newViewDate = this.moveYear(this.viewDate, dir);
21587                 } else if (e.shiftKey){
21588                     newDate = this.moveMonth(this.date, dir);
21589                     newViewDate = this.moveMonth(this.viewDate, dir);
21590                 } else {
21591                     newDate = new Date(this.date);
21592                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21593                     newViewDate = new Date(this.viewDate);
21594                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21595                 }
21596                 if (this.dateWithinRange(newDate)){
21597                     this.date = newDate;
21598                     this.viewDate = newViewDate;
21599                     this.setValue(this.formatDate(this.date));
21600 //                    this.update();
21601                     e.preventDefault();
21602                     dateChanged = true;
21603                 }
21604                 break;
21605             case 38: // up
21606             case 40: // down
21607                 if (!this.keyboardNavigation) {
21608                     break;
21609                 }
21610                 dir = e.keyCode == 38 ? -1 : 1;
21611                 if (e.ctrlKey){
21612                     newDate = this.moveYear(this.date, dir);
21613                     newViewDate = this.moveYear(this.viewDate, dir);
21614                 } else if (e.shiftKey){
21615                     newDate = this.moveMonth(this.date, dir);
21616                     newViewDate = this.moveMonth(this.viewDate, dir);
21617                 } else {
21618                     newDate = new Date(this.date);
21619                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21620                     newViewDate = new Date(this.viewDate);
21621                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21622                 }
21623                 if (this.dateWithinRange(newDate)){
21624                     this.date = newDate;
21625                     this.viewDate = newViewDate;
21626                     this.setValue(this.formatDate(this.date));
21627 //                    this.update();
21628                     e.preventDefault();
21629                     dateChanged = true;
21630                 }
21631                 break;
21632             case 13: // enter
21633                 this.setValue(this.formatDate(this.date));
21634                 this.hidePopup();
21635                 e.preventDefault();
21636                 break;
21637             case 9: // tab
21638                 this.setValue(this.formatDate(this.date));
21639                 this.hidePopup();
21640                 break;
21641             case 16: // shift
21642             case 17: // ctrl
21643             case 18: // alt
21644                 break;
21645             default :
21646                 this.hidePopup();
21647                 
21648         }
21649     },
21650     
21651     
21652     onClick: function(e) 
21653     {
21654         e.stopPropagation();
21655         e.preventDefault();
21656         
21657         var target = e.getTarget();
21658         
21659         if(target.nodeName.toLowerCase() === 'i'){
21660             target = Roo.get(target).dom.parentNode;
21661         }
21662         
21663         var nodeName = target.nodeName;
21664         var className = target.className;
21665         var html = target.innerHTML;
21666         //Roo.log(nodeName);
21667         
21668         switch(nodeName.toLowerCase()) {
21669             case 'th':
21670                 switch(className) {
21671                     case 'switch':
21672                         this.showMode(1);
21673                         break;
21674                     case 'prev':
21675                     case 'next':
21676                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21677                         switch(this.viewMode){
21678                                 case 0:
21679                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21680                                         break;
21681                                 case 1:
21682                                 case 2:
21683                                         this.viewDate = this.moveYear(this.viewDate, dir);
21684                                         break;
21685                         }
21686                         this.fill();
21687                         break;
21688                     case 'today':
21689                         var date = new Date();
21690                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21691 //                        this.fill()
21692                         this.setValue(this.formatDate(this.date));
21693                         
21694                         this.hidePopup();
21695                         break;
21696                 }
21697                 break;
21698             case 'span':
21699                 if (className.indexOf('disabled') < 0) {
21700                     this.viewDate.setUTCDate(1);
21701                     if (className.indexOf('month') > -1) {
21702                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21703                     } else {
21704                         var year = parseInt(html, 10) || 0;
21705                         this.viewDate.setUTCFullYear(year);
21706                         
21707                     }
21708                     
21709                     if(this.singleMode){
21710                         this.setValue(this.formatDate(this.viewDate));
21711                         this.hidePopup();
21712                         return;
21713                     }
21714                     
21715                     this.showMode(-1);
21716                     this.fill();
21717                 }
21718                 break;
21719                 
21720             case 'td':
21721                 //Roo.log(className);
21722                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21723                     var day = parseInt(html, 10) || 1;
21724                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21725                         month = (this.viewDate || new Date()).getUTCMonth();
21726
21727                     if (className.indexOf('old') > -1) {
21728                         if(month === 0 ){
21729                             month = 11;
21730                             year -= 1;
21731                         }else{
21732                             month -= 1;
21733                         }
21734                     } else if (className.indexOf('new') > -1) {
21735                         if (month == 11) {
21736                             month = 0;
21737                             year += 1;
21738                         } else {
21739                             month += 1;
21740                         }
21741                     }
21742                     //Roo.log([year,month,day]);
21743                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21744                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21745 //                    this.fill();
21746                     //Roo.log(this.formatDate(this.date));
21747                     this.setValue(this.formatDate(this.date));
21748                     this.hidePopup();
21749                 }
21750                 break;
21751         }
21752     },
21753     
21754     setStartDate: function(startDate)
21755     {
21756         this.startDate = startDate || -Infinity;
21757         if (this.startDate !== -Infinity) {
21758             this.startDate = this.parseDate(this.startDate);
21759         }
21760         this.update();
21761         this.updateNavArrows();
21762     },
21763
21764     setEndDate: function(endDate)
21765     {
21766         this.endDate = endDate || Infinity;
21767         if (this.endDate !== Infinity) {
21768             this.endDate = this.parseDate(this.endDate);
21769         }
21770         this.update();
21771         this.updateNavArrows();
21772     },
21773     
21774     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21775     {
21776         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21777         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21778             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21779         }
21780         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21781             return parseInt(d, 10);
21782         });
21783         this.update();
21784         this.updateNavArrows();
21785     },
21786     
21787     updateNavArrows: function() 
21788     {
21789         if(this.singleMode){
21790             return;
21791         }
21792         
21793         var d = new Date(this.viewDate),
21794         year = d.getUTCFullYear(),
21795         month = d.getUTCMonth();
21796         
21797         Roo.each(this.picker().select('.prev', true).elements, function(v){
21798             v.show();
21799             switch (this.viewMode) {
21800                 case 0:
21801
21802                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21803                         v.hide();
21804                     }
21805                     break;
21806                 case 1:
21807                 case 2:
21808                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21809                         v.hide();
21810                     }
21811                     break;
21812             }
21813         });
21814         
21815         Roo.each(this.picker().select('.next', true).elements, function(v){
21816             v.show();
21817             switch (this.viewMode) {
21818                 case 0:
21819
21820                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21821                         v.hide();
21822                     }
21823                     break;
21824                 case 1:
21825                 case 2:
21826                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21827                         v.hide();
21828                     }
21829                     break;
21830             }
21831         })
21832     },
21833     
21834     moveMonth: function(date, dir)
21835     {
21836         if (!dir) {
21837             return date;
21838         }
21839         var new_date = new Date(date.valueOf()),
21840         day = new_date.getUTCDate(),
21841         month = new_date.getUTCMonth(),
21842         mag = Math.abs(dir),
21843         new_month, test;
21844         dir = dir > 0 ? 1 : -1;
21845         if (mag == 1){
21846             test = dir == -1
21847             // If going back one month, make sure month is not current month
21848             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21849             ? function(){
21850                 return new_date.getUTCMonth() == month;
21851             }
21852             // If going forward one month, make sure month is as expected
21853             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21854             : function(){
21855                 return new_date.getUTCMonth() != new_month;
21856             };
21857             new_month = month + dir;
21858             new_date.setUTCMonth(new_month);
21859             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21860             if (new_month < 0 || new_month > 11) {
21861                 new_month = (new_month + 12) % 12;
21862             }
21863         } else {
21864             // For magnitudes >1, move one month at a time...
21865             for (var i=0; i<mag; i++) {
21866                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21867                 new_date = this.moveMonth(new_date, dir);
21868             }
21869             // ...then reset the day, keeping it in the new month
21870             new_month = new_date.getUTCMonth();
21871             new_date.setUTCDate(day);
21872             test = function(){
21873                 return new_month != new_date.getUTCMonth();
21874             };
21875         }
21876         // Common date-resetting loop -- if date is beyond end of month, make it
21877         // end of month
21878         while (test()){
21879             new_date.setUTCDate(--day);
21880             new_date.setUTCMonth(new_month);
21881         }
21882         return new_date;
21883     },
21884
21885     moveYear: function(date, dir)
21886     {
21887         return this.moveMonth(date, dir*12);
21888     },
21889
21890     dateWithinRange: function(date)
21891     {
21892         return date >= this.startDate && date <= this.endDate;
21893     },
21894
21895     
21896     remove: function() 
21897     {
21898         this.picker().remove();
21899     },
21900     
21901     validateValue : function(value)
21902     {
21903         if(this.getVisibilityEl().hasClass('hidden')){
21904             return true;
21905         }
21906         
21907         if(value.length < 1)  {
21908             if(this.allowBlank){
21909                 return true;
21910             }
21911             return false;
21912         }
21913         
21914         if(value.length < this.minLength){
21915             return false;
21916         }
21917         if(value.length > this.maxLength){
21918             return false;
21919         }
21920         if(this.vtype){
21921             var vt = Roo.form.VTypes;
21922             if(!vt[this.vtype](value, this)){
21923                 return false;
21924             }
21925         }
21926         if(typeof this.validator == "function"){
21927             var msg = this.validator(value);
21928             if(msg !== true){
21929                 return false;
21930             }
21931         }
21932         
21933         if(this.regex && !this.regex.test(value)){
21934             return false;
21935         }
21936         
21937         if(typeof(this.parseDate(value)) == 'undefined'){
21938             return false;
21939         }
21940         
21941         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21942             return false;
21943         }      
21944         
21945         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21946             return false;
21947         } 
21948         
21949         
21950         return true;
21951     },
21952     
21953     reset : function()
21954     {
21955         this.date = this.viewDate = '';
21956         
21957         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21958     }
21959    
21960 });
21961
21962 Roo.apply(Roo.bootstrap.DateField,  {
21963     
21964     head : {
21965         tag: 'thead',
21966         cn: [
21967         {
21968             tag: 'tr',
21969             cn: [
21970             {
21971                 tag: 'th',
21972                 cls: 'prev',
21973                 html: '<i class="fa fa-arrow-left"/>'
21974             },
21975             {
21976                 tag: 'th',
21977                 cls: 'switch',
21978                 colspan: '5'
21979             },
21980             {
21981                 tag: 'th',
21982                 cls: 'next',
21983                 html: '<i class="fa fa-arrow-right"/>'
21984             }
21985
21986             ]
21987         }
21988         ]
21989     },
21990     
21991     content : {
21992         tag: 'tbody',
21993         cn: [
21994         {
21995             tag: 'tr',
21996             cn: [
21997             {
21998                 tag: 'td',
21999                 colspan: '7'
22000             }
22001             ]
22002         }
22003         ]
22004     },
22005     
22006     footer : {
22007         tag: 'tfoot',
22008         cn: [
22009         {
22010             tag: 'tr',
22011             cn: [
22012             {
22013                 tag: 'th',
22014                 colspan: '7',
22015                 cls: 'today'
22016             }
22017                     
22018             ]
22019         }
22020         ]
22021     },
22022     
22023     dates:{
22024         en: {
22025             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22026             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22027             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22028             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22029             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22030             today: "Today"
22031         }
22032     },
22033     
22034     modes: [
22035     {
22036         clsName: 'days',
22037         navFnc: 'Month',
22038         navStep: 1
22039     },
22040     {
22041         clsName: 'months',
22042         navFnc: 'FullYear',
22043         navStep: 1
22044     },
22045     {
22046         clsName: 'years',
22047         navFnc: 'FullYear',
22048         navStep: 10
22049     }]
22050 });
22051
22052 Roo.apply(Roo.bootstrap.DateField,  {
22053   
22054     template : {
22055         tag: 'div',
22056         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22057         cn: [
22058         {
22059             tag: 'div',
22060             cls: 'datepicker-days',
22061             cn: [
22062             {
22063                 tag: 'table',
22064                 cls: 'table-condensed',
22065                 cn:[
22066                 Roo.bootstrap.DateField.head,
22067                 {
22068                     tag: 'tbody'
22069                 },
22070                 Roo.bootstrap.DateField.footer
22071                 ]
22072             }
22073             ]
22074         },
22075         {
22076             tag: 'div',
22077             cls: 'datepicker-months',
22078             cn: [
22079             {
22080                 tag: 'table',
22081                 cls: 'table-condensed',
22082                 cn:[
22083                 Roo.bootstrap.DateField.head,
22084                 Roo.bootstrap.DateField.content,
22085                 Roo.bootstrap.DateField.footer
22086                 ]
22087             }
22088             ]
22089         },
22090         {
22091             tag: 'div',
22092             cls: 'datepicker-years',
22093             cn: [
22094             {
22095                 tag: 'table',
22096                 cls: 'table-condensed',
22097                 cn:[
22098                 Roo.bootstrap.DateField.head,
22099                 Roo.bootstrap.DateField.content,
22100                 Roo.bootstrap.DateField.footer
22101                 ]
22102             }
22103             ]
22104         }
22105         ]
22106     }
22107 });
22108
22109  
22110
22111  /*
22112  * - LGPL
22113  *
22114  * TimeField
22115  * 
22116  */
22117
22118 /**
22119  * @class Roo.bootstrap.TimeField
22120  * @extends Roo.bootstrap.Input
22121  * Bootstrap DateField class
22122  * 
22123  * 
22124  * @constructor
22125  * Create a new TimeField
22126  * @param {Object} config The config object
22127  */
22128
22129 Roo.bootstrap.TimeField = function(config){
22130     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22131     this.addEvents({
22132             /**
22133              * @event show
22134              * Fires when this field show.
22135              * @param {Roo.bootstrap.DateField} thisthis
22136              * @param {Mixed} date The date value
22137              */
22138             show : true,
22139             /**
22140              * @event show
22141              * Fires when this field hide.
22142              * @param {Roo.bootstrap.DateField} this
22143              * @param {Mixed} date The date value
22144              */
22145             hide : true,
22146             /**
22147              * @event select
22148              * Fires when select a date.
22149              * @param {Roo.bootstrap.DateField} this
22150              * @param {Mixed} date The date value
22151              */
22152             select : true
22153         });
22154 };
22155
22156 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22157     
22158     /**
22159      * @cfg {String} format
22160      * The default time format string which can be overriden for localization support.  The format must be
22161      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22162      */
22163     format : "H:i",
22164
22165     getAutoCreate : function()
22166     {
22167         this.after = '<i class="fa far fa-clock"></i>';
22168         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22169         
22170          
22171     },
22172     onRender: function(ct, position)
22173     {
22174         
22175         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22176                 
22177         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22178         
22179         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22180         
22181         this.pop = this.picker().select('>.datepicker-time',true).first();
22182         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22183         
22184         this.picker().on('mousedown', this.onMousedown, this);
22185         this.picker().on('click', this.onClick, this);
22186         
22187         this.picker().addClass('datepicker-dropdown');
22188     
22189         this.fillTime();
22190         this.update();
22191             
22192         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22193         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22194         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22195         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22196         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22197         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22198
22199     },
22200     
22201     fireKey: function(e){
22202         if (!this.picker().isVisible()){
22203             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22204                 this.show();
22205             }
22206             return;
22207         }
22208
22209         e.preventDefault();
22210         
22211         switch(e.keyCode){
22212             case 27: // escape
22213                 this.hide();
22214                 break;
22215             case 37: // left
22216             case 39: // right
22217                 this.onTogglePeriod();
22218                 break;
22219             case 38: // up
22220                 this.onIncrementMinutes();
22221                 break;
22222             case 40: // down
22223                 this.onDecrementMinutes();
22224                 break;
22225             case 13: // enter
22226             case 9: // tab
22227                 this.setTime();
22228                 break;
22229         }
22230     },
22231     
22232     onClick: function(e) {
22233         e.stopPropagation();
22234         e.preventDefault();
22235     },
22236     
22237     picker : function()
22238     {
22239         return this.pickerEl;
22240     },
22241     
22242     fillTime: function()
22243     {    
22244         var time = this.pop.select('tbody', true).first();
22245         
22246         time.dom.innerHTML = '';
22247         
22248         time.createChild({
22249             tag: 'tr',
22250             cn: [
22251                 {
22252                     tag: 'td',
22253                     cn: [
22254                         {
22255                             tag: 'a',
22256                             href: '#',
22257                             cls: 'btn',
22258                             cn: [
22259                                 {
22260                                     tag: 'i',
22261                                     cls: 'hours-up fa fas fa-chevron-up'
22262                                 }
22263                             ]
22264                         } 
22265                     ]
22266                 },
22267                 {
22268                     tag: 'td',
22269                     cls: 'separator'
22270                 },
22271                 {
22272                     tag: 'td',
22273                     cn: [
22274                         {
22275                             tag: 'a',
22276                             href: '#',
22277                             cls: 'btn',
22278                             cn: [
22279                                 {
22280                                     tag: 'i',
22281                                     cls: 'minutes-up fa fas fa-chevron-up'
22282                                 }
22283                             ]
22284                         }
22285                     ]
22286                 },
22287                 {
22288                     tag: 'td',
22289                     cls: 'separator'
22290                 }
22291             ]
22292         });
22293         
22294         time.createChild({
22295             tag: 'tr',
22296             cn: [
22297                 {
22298                     tag: 'td',
22299                     cn: [
22300                         {
22301                             tag: 'span',
22302                             cls: 'timepicker-hour',
22303                             html: '00'
22304                         }  
22305                     ]
22306                 },
22307                 {
22308                     tag: 'td',
22309                     cls: 'separator',
22310                     html: ':'
22311                 },
22312                 {
22313                     tag: 'td',
22314                     cn: [
22315                         {
22316                             tag: 'span',
22317                             cls: 'timepicker-minute',
22318                             html: '00'
22319                         }  
22320                     ]
22321                 },
22322                 {
22323                     tag: 'td',
22324                     cls: 'separator'
22325                 },
22326                 {
22327                     tag: 'td',
22328                     cn: [
22329                         {
22330                             tag: 'button',
22331                             type: 'button',
22332                             cls: 'btn btn-primary period',
22333                             html: 'AM'
22334                             
22335                         }
22336                     ]
22337                 }
22338             ]
22339         });
22340         
22341         time.createChild({
22342             tag: 'tr',
22343             cn: [
22344                 {
22345                     tag: 'td',
22346                     cn: [
22347                         {
22348                             tag: 'a',
22349                             href: '#',
22350                             cls: 'btn',
22351                             cn: [
22352                                 {
22353                                     tag: 'span',
22354                                     cls: 'hours-down fa fas fa-chevron-down'
22355                                 }
22356                             ]
22357                         }
22358                     ]
22359                 },
22360                 {
22361                     tag: 'td',
22362                     cls: 'separator'
22363                 },
22364                 {
22365                     tag: 'td',
22366                     cn: [
22367                         {
22368                             tag: 'a',
22369                             href: '#',
22370                             cls: 'btn',
22371                             cn: [
22372                                 {
22373                                     tag: 'span',
22374                                     cls: 'minutes-down fa fas fa-chevron-down'
22375                                 }
22376                             ]
22377                         }
22378                     ]
22379                 },
22380                 {
22381                     tag: 'td',
22382                     cls: 'separator'
22383                 }
22384             ]
22385         });
22386         
22387     },
22388     
22389     update: function()
22390     {
22391         
22392         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22393         
22394         this.fill();
22395     },
22396     
22397     fill: function() 
22398     {
22399         var hours = this.time.getHours();
22400         var minutes = this.time.getMinutes();
22401         var period = 'AM';
22402         
22403         if(hours > 11){
22404             period = 'PM';
22405         }
22406         
22407         if(hours == 0){
22408             hours = 12;
22409         }
22410         
22411         
22412         if(hours > 12){
22413             hours = hours - 12;
22414         }
22415         
22416         if(hours < 10){
22417             hours = '0' + hours;
22418         }
22419         
22420         if(minutes < 10){
22421             minutes = '0' + minutes;
22422         }
22423         
22424         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22425         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22426         this.pop.select('button', true).first().dom.innerHTML = period;
22427         
22428     },
22429     
22430     place: function()
22431     {   
22432         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22433         
22434         var cls = ['bottom'];
22435         
22436         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22437             cls.pop();
22438             cls.push('top');
22439         }
22440         
22441         cls.push('right');
22442         
22443         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22444             cls.pop();
22445             cls.push('left');
22446         }
22447         //this.picker().setXY(20000,20000);
22448         this.picker().addClass(cls.join('-'));
22449         
22450         var _this = this;
22451         
22452         Roo.each(cls, function(c){
22453             if(c == 'bottom'){
22454                 (function() {
22455                  //  
22456                 }).defer(200);
22457                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22458                 //_this.picker().setTop(_this.inputEl().getHeight());
22459                 return;
22460             }
22461             if(c == 'top'){
22462                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22463                 
22464                 //_this.picker().setTop(0 - _this.picker().getHeight());
22465                 return;
22466             }
22467             /*
22468             if(c == 'left'){
22469                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22470                 return;
22471             }
22472             if(c == 'right'){
22473                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22474                 return;
22475             }
22476             */
22477         });
22478         
22479     },
22480   
22481     onFocus : function()
22482     {
22483         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22484         this.show();
22485     },
22486     
22487     onBlur : function()
22488     {
22489         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22490         this.hide();
22491     },
22492     
22493     show : function()
22494     {
22495         this.picker().show();
22496         this.pop.show();
22497         this.update();
22498         this.place();
22499         
22500         this.fireEvent('show', this, this.date);
22501     },
22502     
22503     hide : function()
22504     {
22505         this.picker().hide();
22506         this.pop.hide();
22507         
22508         this.fireEvent('hide', this, this.date);
22509     },
22510     
22511     setTime : function()
22512     {
22513         this.hide();
22514         this.setValue(this.time.format(this.format));
22515         
22516         this.fireEvent('select', this, this.date);
22517         
22518         
22519     },
22520     
22521     onMousedown: function(e){
22522         e.stopPropagation();
22523         e.preventDefault();
22524     },
22525     
22526     onIncrementHours: function()
22527     {
22528         Roo.log('onIncrementHours');
22529         this.time = this.time.add(Date.HOUR, 1);
22530         this.update();
22531         
22532     },
22533     
22534     onDecrementHours: function()
22535     {
22536         Roo.log('onDecrementHours');
22537         this.time = this.time.add(Date.HOUR, -1);
22538         this.update();
22539     },
22540     
22541     onIncrementMinutes: function()
22542     {
22543         Roo.log('onIncrementMinutes');
22544         this.time = this.time.add(Date.MINUTE, 1);
22545         this.update();
22546     },
22547     
22548     onDecrementMinutes: function()
22549     {
22550         Roo.log('onDecrementMinutes');
22551         this.time = this.time.add(Date.MINUTE, -1);
22552         this.update();
22553     },
22554     
22555     onTogglePeriod: function()
22556     {
22557         Roo.log('onTogglePeriod');
22558         this.time = this.time.add(Date.HOUR, 12);
22559         this.update();
22560     }
22561     
22562    
22563 });
22564  
22565
22566 Roo.apply(Roo.bootstrap.TimeField,  {
22567   
22568     template : {
22569         tag: 'div',
22570         cls: 'datepicker dropdown-menu',
22571         cn: [
22572             {
22573                 tag: 'div',
22574                 cls: 'datepicker-time',
22575                 cn: [
22576                 {
22577                     tag: 'table',
22578                     cls: 'table-condensed',
22579                     cn:[
22580                         {
22581                             tag: 'tbody',
22582                             cn: [
22583                                 {
22584                                     tag: 'tr',
22585                                     cn: [
22586                                     {
22587                                         tag: 'td',
22588                                         colspan: '7'
22589                                     }
22590                                     ]
22591                                 }
22592                             ]
22593                         },
22594                         {
22595                             tag: 'tfoot',
22596                             cn: [
22597                                 {
22598                                     tag: 'tr',
22599                                     cn: [
22600                                     {
22601                                         tag: 'th',
22602                                         colspan: '7',
22603                                         cls: '',
22604                                         cn: [
22605                                             {
22606                                                 tag: 'button',
22607                                                 cls: 'btn btn-info ok',
22608                                                 html: 'OK'
22609                                             }
22610                                         ]
22611                                     }
22612                     
22613                                     ]
22614                                 }
22615                             ]
22616                         }
22617                     ]
22618                 }
22619                 ]
22620             }
22621         ]
22622     }
22623 });
22624
22625  
22626
22627  /*
22628  * - LGPL
22629  *
22630  * MonthField
22631  * 
22632  */
22633
22634 /**
22635  * @class Roo.bootstrap.MonthField
22636  * @extends Roo.bootstrap.Input
22637  * Bootstrap MonthField class
22638  * 
22639  * @cfg {String} language default en
22640  * 
22641  * @constructor
22642  * Create a new MonthField
22643  * @param {Object} config The config object
22644  */
22645
22646 Roo.bootstrap.MonthField = function(config){
22647     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22648     
22649     this.addEvents({
22650         /**
22651          * @event show
22652          * Fires when this field show.
22653          * @param {Roo.bootstrap.MonthField} this
22654          * @param {Mixed} date The date value
22655          */
22656         show : true,
22657         /**
22658          * @event show
22659          * Fires when this field hide.
22660          * @param {Roo.bootstrap.MonthField} this
22661          * @param {Mixed} date The date value
22662          */
22663         hide : true,
22664         /**
22665          * @event select
22666          * Fires when select a date.
22667          * @param {Roo.bootstrap.MonthField} this
22668          * @param {String} oldvalue The old value
22669          * @param {String} newvalue The new value
22670          */
22671         select : true
22672     });
22673 };
22674
22675 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22676     
22677     onRender: function(ct, position)
22678     {
22679         
22680         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22681         
22682         this.language = this.language || 'en';
22683         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22684         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22685         
22686         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22687         this.isInline = false;
22688         this.isInput = true;
22689         this.component = this.el.select('.add-on', true).first() || false;
22690         this.component = (this.component && this.component.length === 0) ? false : this.component;
22691         this.hasInput = this.component && this.inputEL().length;
22692         
22693         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22694         
22695         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22696         
22697         this.picker().on('mousedown', this.onMousedown, this);
22698         this.picker().on('click', this.onClick, this);
22699         
22700         this.picker().addClass('datepicker-dropdown');
22701         
22702         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22703             v.setStyle('width', '189px');
22704         });
22705         
22706         this.fillMonths();
22707         
22708         this.update();
22709         
22710         if(this.isInline) {
22711             this.show();
22712         }
22713         
22714     },
22715     
22716     setValue: function(v, suppressEvent)
22717     {   
22718         var o = this.getValue();
22719         
22720         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22721         
22722         this.update();
22723
22724         if(suppressEvent !== true){
22725             this.fireEvent('select', this, o, v);
22726         }
22727         
22728     },
22729     
22730     getValue: function()
22731     {
22732         return this.value;
22733     },
22734     
22735     onClick: function(e) 
22736     {
22737         e.stopPropagation();
22738         e.preventDefault();
22739         
22740         var target = e.getTarget();
22741         
22742         if(target.nodeName.toLowerCase() === 'i'){
22743             target = Roo.get(target).dom.parentNode;
22744         }
22745         
22746         var nodeName = target.nodeName;
22747         var className = target.className;
22748         var html = target.innerHTML;
22749         
22750         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22751             return;
22752         }
22753         
22754         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22755         
22756         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22757         
22758         this.hide();
22759                         
22760     },
22761     
22762     picker : function()
22763     {
22764         return this.pickerEl;
22765     },
22766     
22767     fillMonths: function()
22768     {    
22769         var i = 0;
22770         var months = this.picker().select('>.datepicker-months td', true).first();
22771         
22772         months.dom.innerHTML = '';
22773         
22774         while (i < 12) {
22775             var month = {
22776                 tag: 'span',
22777                 cls: 'month',
22778                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22779             };
22780             
22781             months.createChild(month);
22782         }
22783         
22784     },
22785     
22786     update: function()
22787     {
22788         var _this = this;
22789         
22790         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22791             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22792         }
22793         
22794         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22795             e.removeClass('active');
22796             
22797             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22798                 e.addClass('active');
22799             }
22800         })
22801     },
22802     
22803     place: function()
22804     {
22805         if(this.isInline) {
22806             return;
22807         }
22808         
22809         this.picker().removeClass(['bottom', 'top']);
22810         
22811         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22812             /*
22813              * place to the top of element!
22814              *
22815              */
22816             
22817             this.picker().addClass('top');
22818             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22819             
22820             return;
22821         }
22822         
22823         this.picker().addClass('bottom');
22824         
22825         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22826     },
22827     
22828     onFocus : function()
22829     {
22830         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22831         this.show();
22832     },
22833     
22834     onBlur : function()
22835     {
22836         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22837         
22838         var d = this.inputEl().getValue();
22839         
22840         this.setValue(d);
22841                 
22842         this.hide();
22843     },
22844     
22845     show : function()
22846     {
22847         this.picker().show();
22848         this.picker().select('>.datepicker-months', true).first().show();
22849         this.update();
22850         this.place();
22851         
22852         this.fireEvent('show', this, this.date);
22853     },
22854     
22855     hide : function()
22856     {
22857         if(this.isInline) {
22858             return;
22859         }
22860         this.picker().hide();
22861         this.fireEvent('hide', this, this.date);
22862         
22863     },
22864     
22865     onMousedown: function(e)
22866     {
22867         e.stopPropagation();
22868         e.preventDefault();
22869     },
22870     
22871     keyup: function(e)
22872     {
22873         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22874         this.update();
22875     },
22876
22877     fireKey: function(e)
22878     {
22879         if (!this.picker().isVisible()){
22880             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22881                 this.show();
22882             }
22883             return;
22884         }
22885         
22886         var dir;
22887         
22888         switch(e.keyCode){
22889             case 27: // escape
22890                 this.hide();
22891                 e.preventDefault();
22892                 break;
22893             case 37: // left
22894             case 39: // right
22895                 dir = e.keyCode == 37 ? -1 : 1;
22896                 
22897                 this.vIndex = this.vIndex + dir;
22898                 
22899                 if(this.vIndex < 0){
22900                     this.vIndex = 0;
22901                 }
22902                 
22903                 if(this.vIndex > 11){
22904                     this.vIndex = 11;
22905                 }
22906                 
22907                 if(isNaN(this.vIndex)){
22908                     this.vIndex = 0;
22909                 }
22910                 
22911                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22912                 
22913                 break;
22914             case 38: // up
22915             case 40: // down
22916                 
22917                 dir = e.keyCode == 38 ? -1 : 1;
22918                 
22919                 this.vIndex = this.vIndex + dir * 4;
22920                 
22921                 if(this.vIndex < 0){
22922                     this.vIndex = 0;
22923                 }
22924                 
22925                 if(this.vIndex > 11){
22926                     this.vIndex = 11;
22927                 }
22928                 
22929                 if(isNaN(this.vIndex)){
22930                     this.vIndex = 0;
22931                 }
22932                 
22933                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22934                 break;
22935                 
22936             case 13: // enter
22937                 
22938                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22939                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22940                 }
22941                 
22942                 this.hide();
22943                 e.preventDefault();
22944                 break;
22945             case 9: // tab
22946                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22947                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22948                 }
22949                 this.hide();
22950                 break;
22951             case 16: // shift
22952             case 17: // ctrl
22953             case 18: // alt
22954                 break;
22955             default :
22956                 this.hide();
22957                 
22958         }
22959     },
22960     
22961     remove: function() 
22962     {
22963         this.picker().remove();
22964     }
22965    
22966 });
22967
22968 Roo.apply(Roo.bootstrap.MonthField,  {
22969     
22970     content : {
22971         tag: 'tbody',
22972         cn: [
22973         {
22974             tag: 'tr',
22975             cn: [
22976             {
22977                 tag: 'td',
22978                 colspan: '7'
22979             }
22980             ]
22981         }
22982         ]
22983     },
22984     
22985     dates:{
22986         en: {
22987             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22988             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22989         }
22990     }
22991 });
22992
22993 Roo.apply(Roo.bootstrap.MonthField,  {
22994   
22995     template : {
22996         tag: 'div',
22997         cls: 'datepicker dropdown-menu roo-dynamic',
22998         cn: [
22999             {
23000                 tag: 'div',
23001                 cls: 'datepicker-months',
23002                 cn: [
23003                 {
23004                     tag: 'table',
23005                     cls: 'table-condensed',
23006                     cn:[
23007                         Roo.bootstrap.DateField.content
23008                     ]
23009                 }
23010                 ]
23011             }
23012         ]
23013     }
23014 });
23015
23016  
23017
23018  
23019  /*
23020  * - LGPL
23021  *
23022  * CheckBox
23023  * 
23024  */
23025
23026 /**
23027  * @class Roo.bootstrap.CheckBox
23028  * @extends Roo.bootstrap.Input
23029  * Bootstrap CheckBox class
23030  * 
23031  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23032  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23033  * @cfg {String} boxLabel The text that appears beside the checkbox
23034  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23035  * @cfg {Boolean} checked initnal the element
23036  * @cfg {Boolean} inline inline the element (default false)
23037  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23038  * @cfg {String} tooltip label tooltip
23039  * 
23040  * @constructor
23041  * Create a new CheckBox
23042  * @param {Object} config The config object
23043  */
23044
23045 Roo.bootstrap.CheckBox = function(config){
23046     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23047    
23048     this.addEvents({
23049         /**
23050         * @event check
23051         * Fires when the element is checked or unchecked.
23052         * @param {Roo.bootstrap.CheckBox} this This input
23053         * @param {Boolean} checked The new checked value
23054         */
23055        check : true,
23056        /**
23057         * @event click
23058         * Fires when the element is click.
23059         * @param {Roo.bootstrap.CheckBox} this This input
23060         */
23061        click : true
23062     });
23063     
23064 };
23065
23066 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23067   
23068     inputType: 'checkbox',
23069     inputValue: 1,
23070     valueOff: 0,
23071     boxLabel: false,
23072     checked: false,
23073     weight : false,
23074     inline: false,
23075     tooltip : '',
23076     
23077     // checkbox success does not make any sense really.. 
23078     invalidClass : "",
23079     validClass : "",
23080     
23081     
23082     getAutoCreate : function()
23083     {
23084         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23085         
23086         var id = Roo.id();
23087         
23088         var cfg = {};
23089         
23090         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23091         
23092         if(this.inline){
23093             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23094         }
23095         
23096         var input =  {
23097             tag: 'input',
23098             id : id,
23099             type : this.inputType,
23100             value : this.inputValue,
23101             cls : 'roo-' + this.inputType, //'form-box',
23102             placeholder : this.placeholder || ''
23103             
23104         };
23105         
23106         if(this.inputType != 'radio'){
23107             var hidden =  {
23108                 tag: 'input',
23109                 type : 'hidden',
23110                 cls : 'roo-hidden-value',
23111                 value : this.checked ? this.inputValue : this.valueOff
23112             };
23113         }
23114         
23115             
23116         if (this.weight) { // Validity check?
23117             cfg.cls += " " + this.inputType + "-" + this.weight;
23118         }
23119         
23120         if (this.disabled) {
23121             input.disabled=true;
23122         }
23123         
23124         if(this.checked){
23125             input.checked = this.checked;
23126         }
23127         
23128         if (this.name) {
23129             
23130             input.name = this.name;
23131             
23132             if(this.inputType != 'radio'){
23133                 hidden.name = this.name;
23134                 input.name = '_hidden_' + this.name;
23135             }
23136         }
23137         
23138         if (this.size) {
23139             input.cls += ' input-' + this.size;
23140         }
23141         
23142         var settings=this;
23143         
23144         ['xs','sm','md','lg'].map(function(size){
23145             if (settings[size]) {
23146                 cfg.cls += ' col-' + size + '-' + settings[size];
23147             }
23148         });
23149         
23150         var inputblock = input;
23151          
23152         if (this.before || this.after) {
23153             
23154             inputblock = {
23155                 cls : 'input-group',
23156                 cn :  [] 
23157             };
23158             
23159             if (this.before) {
23160                 inputblock.cn.push({
23161                     tag :'span',
23162                     cls : 'input-group-addon',
23163                     html : this.before
23164                 });
23165             }
23166             
23167             inputblock.cn.push(input);
23168             
23169             if(this.inputType != 'radio'){
23170                 inputblock.cn.push(hidden);
23171             }
23172             
23173             if (this.after) {
23174                 inputblock.cn.push({
23175                     tag :'span',
23176                     cls : 'input-group-addon',
23177                     html : this.after
23178                 });
23179             }
23180             
23181         }
23182         var boxLabelCfg = false;
23183         
23184         if(this.boxLabel){
23185            
23186             boxLabelCfg = {
23187                 tag: 'label',
23188                 //'for': id, // box label is handled by onclick - so no for...
23189                 cls: 'box-label',
23190                 html: this.boxLabel
23191             };
23192             if(this.tooltip){
23193                 boxLabelCfg.tooltip = this.tooltip;
23194             }
23195              
23196         }
23197         
23198         
23199         if (align ==='left' && this.fieldLabel.length) {
23200 //                Roo.log("left and has label");
23201             cfg.cn = [
23202                 {
23203                     tag: 'label',
23204                     'for' :  id,
23205                     cls : 'control-label',
23206                     html : this.fieldLabel
23207                 },
23208                 {
23209                     cls : "", 
23210                     cn: [
23211                         inputblock
23212                     ]
23213                 }
23214             ];
23215             
23216             if (boxLabelCfg) {
23217                 cfg.cn[1].cn.push(boxLabelCfg);
23218             }
23219             
23220             if(this.labelWidth > 12){
23221                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23222             }
23223             
23224             if(this.labelWidth < 13 && this.labelmd == 0){
23225                 this.labelmd = this.labelWidth;
23226             }
23227             
23228             if(this.labellg > 0){
23229                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23230                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23231             }
23232             
23233             if(this.labelmd > 0){
23234                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23235                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23236             }
23237             
23238             if(this.labelsm > 0){
23239                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23240                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23241             }
23242             
23243             if(this.labelxs > 0){
23244                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23245                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23246             }
23247             
23248         } else if ( this.fieldLabel.length) {
23249 //                Roo.log(" label");
23250                 cfg.cn = [
23251                    
23252                     {
23253                         tag: this.boxLabel ? 'span' : 'label',
23254                         'for': id,
23255                         cls: 'control-label box-input-label',
23256                         //cls : 'input-group-addon',
23257                         html : this.fieldLabel
23258                     },
23259                     
23260                     inputblock
23261                     
23262                 ];
23263                 if (boxLabelCfg) {
23264                     cfg.cn.push(boxLabelCfg);
23265                 }
23266
23267         } else {
23268             
23269 //                Roo.log(" no label && no align");
23270                 cfg.cn = [  inputblock ] ;
23271                 if (boxLabelCfg) {
23272                     cfg.cn.push(boxLabelCfg);
23273                 }
23274
23275                 
23276         }
23277         
23278        
23279         
23280         if(this.inputType != 'radio'){
23281             cfg.cn.push(hidden);
23282         }
23283         
23284         return cfg;
23285         
23286     },
23287     
23288     /**
23289      * return the real input element.
23290      */
23291     inputEl: function ()
23292     {
23293         return this.el.select('input.roo-' + this.inputType,true).first();
23294     },
23295     hiddenEl: function ()
23296     {
23297         return this.el.select('input.roo-hidden-value',true).first();
23298     },
23299     
23300     labelEl: function()
23301     {
23302         return this.el.select('label.control-label',true).first();
23303     },
23304     /* depricated... */
23305     
23306     label: function()
23307     {
23308         return this.labelEl();
23309     },
23310     
23311     boxLabelEl: function()
23312     {
23313         return this.el.select('label.box-label',true).first();
23314     },
23315     
23316     initEvents : function()
23317     {
23318 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23319         
23320         this.inputEl().on('click', this.onClick,  this);
23321         
23322         if (this.boxLabel) { 
23323             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23324         }
23325         
23326         this.startValue = this.getValue();
23327         
23328         if(this.groupId){
23329             Roo.bootstrap.CheckBox.register(this);
23330         }
23331     },
23332     
23333     onClick : function(e)
23334     {   
23335         if(this.fireEvent('click', this, e) !== false){
23336             this.setChecked(!this.checked);
23337         }
23338         
23339     },
23340     
23341     setChecked : function(state,suppressEvent)
23342     {
23343         this.startValue = this.getValue();
23344
23345         if(this.inputType == 'radio'){
23346             
23347             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23348                 e.dom.checked = false;
23349             });
23350             
23351             this.inputEl().dom.checked = true;
23352             
23353             this.inputEl().dom.value = this.inputValue;
23354             
23355             if(suppressEvent !== true){
23356                 this.fireEvent('check', this, true);
23357             }
23358             
23359             this.validate();
23360             
23361             return;
23362         }
23363         
23364         this.checked = state;
23365         
23366         this.inputEl().dom.checked = state;
23367         
23368         
23369         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23370         
23371         if(suppressEvent !== true){
23372             this.fireEvent('check', this, state);
23373         }
23374         
23375         this.validate();
23376     },
23377     
23378     getValue : function()
23379     {
23380         if(this.inputType == 'radio'){
23381             return this.getGroupValue();
23382         }
23383         
23384         return this.hiddenEl().dom.value;
23385         
23386     },
23387     
23388     getGroupValue : function()
23389     {
23390         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23391             return '';
23392         }
23393         
23394         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23395     },
23396     
23397     setValue : function(v,suppressEvent)
23398     {
23399         if(this.inputType == 'radio'){
23400             this.setGroupValue(v, suppressEvent);
23401             return;
23402         }
23403         
23404         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23405         
23406         this.validate();
23407     },
23408     
23409     setGroupValue : function(v, suppressEvent)
23410     {
23411         this.startValue = this.getValue();
23412         
23413         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23414             e.dom.checked = false;
23415             
23416             if(e.dom.value == v){
23417                 e.dom.checked = true;
23418             }
23419         });
23420         
23421         if(suppressEvent !== true){
23422             this.fireEvent('check', this, true);
23423         }
23424
23425         this.validate();
23426         
23427         return;
23428     },
23429     
23430     validate : function()
23431     {
23432         if(this.getVisibilityEl().hasClass('hidden')){
23433             return true;
23434         }
23435         
23436         if(
23437                 this.disabled || 
23438                 (this.inputType == 'radio' && this.validateRadio()) ||
23439                 (this.inputType == 'checkbox' && this.validateCheckbox())
23440         ){
23441             this.markValid();
23442             return true;
23443         }
23444         
23445         this.markInvalid();
23446         return false;
23447     },
23448     
23449     validateRadio : function()
23450     {
23451         if(this.getVisibilityEl().hasClass('hidden')){
23452             return true;
23453         }
23454         
23455         if(this.allowBlank){
23456             return true;
23457         }
23458         
23459         var valid = false;
23460         
23461         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23462             if(!e.dom.checked){
23463                 return;
23464             }
23465             
23466             valid = true;
23467             
23468             return false;
23469         });
23470         
23471         return valid;
23472     },
23473     
23474     validateCheckbox : function()
23475     {
23476         if(!this.groupId){
23477             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23478             //return (this.getValue() == this.inputValue) ? true : false;
23479         }
23480         
23481         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23482         
23483         if(!group){
23484             return false;
23485         }
23486         
23487         var r = false;
23488         
23489         for(var i in group){
23490             if(group[i].el.isVisible(true)){
23491                 r = false;
23492                 break;
23493             }
23494             
23495             r = true;
23496         }
23497         
23498         for(var i in group){
23499             if(r){
23500                 break;
23501             }
23502             
23503             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23504         }
23505         
23506         return r;
23507     },
23508     
23509     /**
23510      * Mark this field as valid
23511      */
23512     markValid : function()
23513     {
23514         var _this = this;
23515         
23516         this.fireEvent('valid', this);
23517         
23518         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23519         
23520         if(this.groupId){
23521             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23522         }
23523         
23524         if(label){
23525             label.markValid();
23526         }
23527
23528         if(this.inputType == 'radio'){
23529             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23530                 var fg = e.findParent('.form-group', false, true);
23531                 if (Roo.bootstrap.version == 3) {
23532                     fg.removeClass([_this.invalidClass, _this.validClass]);
23533                     fg.addClass(_this.validClass);
23534                 } else {
23535                     fg.removeClass(['is-valid', 'is-invalid']);
23536                     fg.addClass('is-valid');
23537                 }
23538             });
23539             
23540             return;
23541         }
23542
23543         if(!this.groupId){
23544             var fg = this.el.findParent('.form-group', false, true);
23545             if (Roo.bootstrap.version == 3) {
23546                 fg.removeClass([this.invalidClass, this.validClass]);
23547                 fg.addClass(this.validClass);
23548             } else {
23549                 fg.removeClass(['is-valid', 'is-invalid']);
23550                 fg.addClass('is-valid');
23551             }
23552             return;
23553         }
23554         
23555         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23556         
23557         if(!group){
23558             return;
23559         }
23560         
23561         for(var i in group){
23562             var fg = group[i].el.findParent('.form-group', false, true);
23563             if (Roo.bootstrap.version == 3) {
23564                 fg.removeClass([this.invalidClass, this.validClass]);
23565                 fg.addClass(this.validClass);
23566             } else {
23567                 fg.removeClass(['is-valid', 'is-invalid']);
23568                 fg.addClass('is-valid');
23569             }
23570         }
23571     },
23572     
23573      /**
23574      * Mark this field as invalid
23575      * @param {String} msg The validation message
23576      */
23577     markInvalid : function(msg)
23578     {
23579         if(this.allowBlank){
23580             return;
23581         }
23582         
23583         var _this = this;
23584         
23585         this.fireEvent('invalid', this, msg);
23586         
23587         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23588         
23589         if(this.groupId){
23590             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23591         }
23592         
23593         if(label){
23594             label.markInvalid();
23595         }
23596             
23597         if(this.inputType == 'radio'){
23598             
23599             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23600                 var fg = e.findParent('.form-group', false, true);
23601                 if (Roo.bootstrap.version == 3) {
23602                     fg.removeClass([_this.invalidClass, _this.validClass]);
23603                     fg.addClass(_this.invalidClass);
23604                 } else {
23605                     fg.removeClass(['is-invalid', 'is-valid']);
23606                     fg.addClass('is-invalid');
23607                 }
23608             });
23609             
23610             return;
23611         }
23612         
23613         if(!this.groupId){
23614             var fg = this.el.findParent('.form-group', false, true);
23615             if (Roo.bootstrap.version == 3) {
23616                 fg.removeClass([_this.invalidClass, _this.validClass]);
23617                 fg.addClass(_this.invalidClass);
23618             } else {
23619                 fg.removeClass(['is-invalid', 'is-valid']);
23620                 fg.addClass('is-invalid');
23621             }
23622             return;
23623         }
23624         
23625         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23626         
23627         if(!group){
23628             return;
23629         }
23630         
23631         for(var i in group){
23632             var fg = group[i].el.findParent('.form-group', false, true);
23633             if (Roo.bootstrap.version == 3) {
23634                 fg.removeClass([_this.invalidClass, _this.validClass]);
23635                 fg.addClass(_this.invalidClass);
23636             } else {
23637                 fg.removeClass(['is-invalid', 'is-valid']);
23638                 fg.addClass('is-invalid');
23639             }
23640         }
23641         
23642     },
23643     
23644     clearInvalid : function()
23645     {
23646         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23647         
23648         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23649         
23650         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23651         
23652         if (label && label.iconEl) {
23653             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23654             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23655         }
23656     },
23657     
23658     disable : function()
23659     {
23660         if(this.inputType != 'radio'){
23661             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23662             return;
23663         }
23664         
23665         var _this = this;
23666         
23667         if(this.rendered){
23668             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23669                 _this.getActionEl().addClass(this.disabledClass);
23670                 e.dom.disabled = true;
23671             });
23672         }
23673         
23674         this.disabled = true;
23675         this.fireEvent("disable", this);
23676         return this;
23677     },
23678
23679     enable : function()
23680     {
23681         if(this.inputType != 'radio'){
23682             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23683             return;
23684         }
23685         
23686         var _this = this;
23687         
23688         if(this.rendered){
23689             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23690                 _this.getActionEl().removeClass(this.disabledClass);
23691                 e.dom.disabled = false;
23692             });
23693         }
23694         
23695         this.disabled = false;
23696         this.fireEvent("enable", this);
23697         return this;
23698     },
23699     
23700     setBoxLabel : function(v)
23701     {
23702         this.boxLabel = v;
23703         
23704         if(this.rendered){
23705             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23706         }
23707     }
23708
23709 });
23710
23711 Roo.apply(Roo.bootstrap.CheckBox, {
23712     
23713     groups: {},
23714     
23715      /**
23716     * register a CheckBox Group
23717     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23718     */
23719     register : function(checkbox)
23720     {
23721         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23722             this.groups[checkbox.groupId] = {};
23723         }
23724         
23725         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23726             return;
23727         }
23728         
23729         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23730         
23731     },
23732     /**
23733     * fetch a CheckBox Group based on the group ID
23734     * @param {string} the group ID
23735     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23736     */
23737     get: function(groupId) {
23738         if (typeof(this.groups[groupId]) == 'undefined') {
23739             return false;
23740         }
23741         
23742         return this.groups[groupId] ;
23743     }
23744     
23745     
23746 });
23747 /*
23748  * - LGPL
23749  *
23750  * RadioItem
23751  * 
23752  */
23753
23754 /**
23755  * @class Roo.bootstrap.Radio
23756  * @extends Roo.bootstrap.Component
23757  * Bootstrap Radio class
23758  * @cfg {String} boxLabel - the label associated
23759  * @cfg {String} value - the value of radio
23760  * 
23761  * @constructor
23762  * Create a new Radio
23763  * @param {Object} config The config object
23764  */
23765 Roo.bootstrap.Radio = function(config){
23766     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23767     
23768 };
23769
23770 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23771     
23772     boxLabel : '',
23773     
23774     value : '',
23775     
23776     getAutoCreate : function()
23777     {
23778         var cfg = {
23779             tag : 'div',
23780             cls : 'form-group radio',
23781             cn : [
23782                 {
23783                     tag : 'label',
23784                     cls : 'box-label',
23785                     html : this.boxLabel
23786                 }
23787             ]
23788         };
23789         
23790         return cfg;
23791     },
23792     
23793     initEvents : function() 
23794     {
23795         this.parent().register(this);
23796         
23797         this.el.on('click', this.onClick, this);
23798         
23799     },
23800     
23801     onClick : function(e)
23802     {
23803         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23804             this.setChecked(true);
23805         }
23806     },
23807     
23808     setChecked : function(state, suppressEvent)
23809     {
23810         this.parent().setValue(this.value, suppressEvent);
23811         
23812     },
23813     
23814     setBoxLabel : function(v)
23815     {
23816         this.boxLabel = v;
23817         
23818         if(this.rendered){
23819             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23820         }
23821     }
23822     
23823 });
23824  
23825
23826  /*
23827  * - LGPL
23828  *
23829  * Input
23830  * 
23831  */
23832
23833 /**
23834  * @class Roo.bootstrap.SecurePass
23835  * @extends Roo.bootstrap.Input
23836  * Bootstrap SecurePass class
23837  *
23838  * 
23839  * @constructor
23840  * Create a new SecurePass
23841  * @param {Object} config The config object
23842  */
23843  
23844 Roo.bootstrap.SecurePass = function (config) {
23845     // these go here, so the translation tool can replace them..
23846     this.errors = {
23847         PwdEmpty: "Please type a password, and then retype it to confirm.",
23848         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23849         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23850         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23851         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23852         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23853         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23854         TooWeak: "Your password is Too Weak."
23855     },
23856     this.meterLabel = "Password strength:";
23857     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23858     this.meterClass = [
23859         "roo-password-meter-tooweak", 
23860         "roo-password-meter-weak", 
23861         "roo-password-meter-medium", 
23862         "roo-password-meter-strong", 
23863         "roo-password-meter-grey"
23864     ];
23865     
23866     this.errors = {};
23867     
23868     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23869 }
23870
23871 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23872     /**
23873      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23874      * {
23875      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23876      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23877      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23878      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23879      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23880      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23881      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23882      * })
23883      */
23884     // private
23885     
23886     meterWidth: 300,
23887     errorMsg :'',    
23888     errors: false,
23889     imageRoot: '/',
23890     /**
23891      * @cfg {String/Object} Label for the strength meter (defaults to
23892      * 'Password strength:')
23893      */
23894     // private
23895     meterLabel: '',
23896     /**
23897      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23898      * ['Weak', 'Medium', 'Strong'])
23899      */
23900     // private    
23901     pwdStrengths: false,    
23902     // private
23903     strength: 0,
23904     // private
23905     _lastPwd: null,
23906     // private
23907     kCapitalLetter: 0,
23908     kSmallLetter: 1,
23909     kDigit: 2,
23910     kPunctuation: 3,
23911     
23912     insecure: false,
23913     // private
23914     initEvents: function ()
23915     {
23916         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23917
23918         if (this.el.is('input[type=password]') && Roo.isSafari) {
23919             this.el.on('keydown', this.SafariOnKeyDown, this);
23920         }
23921
23922         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23923     },
23924     // private
23925     onRender: function (ct, position)
23926     {
23927         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23928         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23929         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23930
23931         this.trigger.createChild({
23932                    cn: [
23933                     {
23934                     //id: 'PwdMeter',
23935                     tag: 'div',
23936                     cls: 'roo-password-meter-grey col-xs-12',
23937                     style: {
23938                         //width: 0,
23939                         //width: this.meterWidth + 'px'                                                
23940                         }
23941                     },
23942                     {                            
23943                          cls: 'roo-password-meter-text'                          
23944                     }
23945                 ]            
23946         });
23947
23948          
23949         if (this.hideTrigger) {
23950             this.trigger.setDisplayed(false);
23951         }
23952         this.setSize(this.width || '', this.height || '');
23953     },
23954     // private
23955     onDestroy: function ()
23956     {
23957         if (this.trigger) {
23958             this.trigger.removeAllListeners();
23959             this.trigger.remove();
23960         }
23961         if (this.wrap) {
23962             this.wrap.remove();
23963         }
23964         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23965     },
23966     // private
23967     checkStrength: function ()
23968     {
23969         var pwd = this.inputEl().getValue();
23970         if (pwd == this._lastPwd) {
23971             return;
23972         }
23973
23974         var strength;
23975         if (this.ClientSideStrongPassword(pwd)) {
23976             strength = 3;
23977         } else if (this.ClientSideMediumPassword(pwd)) {
23978             strength = 2;
23979         } else if (this.ClientSideWeakPassword(pwd)) {
23980             strength = 1;
23981         } else {
23982             strength = 0;
23983         }
23984         
23985         Roo.log('strength1: ' + strength);
23986         
23987         //var pm = this.trigger.child('div/div/div').dom;
23988         var pm = this.trigger.child('div/div');
23989         pm.removeClass(this.meterClass);
23990         pm.addClass(this.meterClass[strength]);
23991                 
23992         
23993         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23994                 
23995         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23996         
23997         this._lastPwd = pwd;
23998     },
23999     reset: function ()
24000     {
24001         Roo.bootstrap.SecurePass.superclass.reset.call(this);
24002         
24003         this._lastPwd = '';
24004         
24005         var pm = this.trigger.child('div/div');
24006         pm.removeClass(this.meterClass);
24007         pm.addClass('roo-password-meter-grey');        
24008         
24009         
24010         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24011         
24012         pt.innerHTML = '';
24013         this.inputEl().dom.type='password';
24014     },
24015     // private
24016     validateValue: function (value)
24017     {
24018         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24019             return false;
24020         }
24021         if (value.length == 0) {
24022             if (this.allowBlank) {
24023                 this.clearInvalid();
24024                 return true;
24025             }
24026
24027             this.markInvalid(this.errors.PwdEmpty);
24028             this.errorMsg = this.errors.PwdEmpty;
24029             return false;
24030         }
24031         
24032         if(this.insecure){
24033             return true;
24034         }
24035         
24036         if (!value.match(/[\x21-\x7e]+/)) {
24037             this.markInvalid(this.errors.PwdBadChar);
24038             this.errorMsg = this.errors.PwdBadChar;
24039             return false;
24040         }
24041         if (value.length < 6) {
24042             this.markInvalid(this.errors.PwdShort);
24043             this.errorMsg = this.errors.PwdShort;
24044             return false;
24045         }
24046         if (value.length > 16) {
24047             this.markInvalid(this.errors.PwdLong);
24048             this.errorMsg = this.errors.PwdLong;
24049             return false;
24050         }
24051         var strength;
24052         if (this.ClientSideStrongPassword(value)) {
24053             strength = 3;
24054         } else if (this.ClientSideMediumPassword(value)) {
24055             strength = 2;
24056         } else if (this.ClientSideWeakPassword(value)) {
24057             strength = 1;
24058         } else {
24059             strength = 0;
24060         }
24061
24062         
24063         if (strength < 2) {
24064             //this.markInvalid(this.errors.TooWeak);
24065             this.errorMsg = this.errors.TooWeak;
24066             //return false;
24067         }
24068         
24069         
24070         console.log('strength2: ' + strength);
24071         
24072         //var pm = this.trigger.child('div/div/div').dom;
24073         
24074         var pm = this.trigger.child('div/div');
24075         pm.removeClass(this.meterClass);
24076         pm.addClass(this.meterClass[strength]);
24077                 
24078         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24079                 
24080         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24081         
24082         this.errorMsg = ''; 
24083         return true;
24084     },
24085     // private
24086     CharacterSetChecks: function (type)
24087     {
24088         this.type = type;
24089         this.fResult = false;
24090     },
24091     // private
24092     isctype: function (character, type)
24093     {
24094         switch (type) {  
24095             case this.kCapitalLetter:
24096                 if (character >= 'A' && character <= 'Z') {
24097                     return true;
24098                 }
24099                 break;
24100             
24101             case this.kSmallLetter:
24102                 if (character >= 'a' && character <= 'z') {
24103                     return true;
24104                 }
24105                 break;
24106             
24107             case this.kDigit:
24108                 if (character >= '0' && character <= '9') {
24109                     return true;
24110                 }
24111                 break;
24112             
24113             case this.kPunctuation:
24114                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24115                     return true;
24116                 }
24117                 break;
24118             
24119             default:
24120                 return false;
24121         }
24122
24123     },
24124     // private
24125     IsLongEnough: function (pwd, size)
24126     {
24127         return !(pwd == null || isNaN(size) || pwd.length < size);
24128     },
24129     // private
24130     SpansEnoughCharacterSets: function (word, nb)
24131     {
24132         if (!this.IsLongEnough(word, nb))
24133         {
24134             return false;
24135         }
24136
24137         var characterSetChecks = new Array(
24138             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24139             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24140         );
24141         
24142         for (var index = 0; index < word.length; ++index) {
24143             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24144                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24145                     characterSetChecks[nCharSet].fResult = true;
24146                     break;
24147                 }
24148             }
24149         }
24150
24151         var nCharSets = 0;
24152         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24153             if (characterSetChecks[nCharSet].fResult) {
24154                 ++nCharSets;
24155             }
24156         }
24157
24158         if (nCharSets < nb) {
24159             return false;
24160         }
24161         return true;
24162     },
24163     // private
24164     ClientSideStrongPassword: function (pwd)
24165     {
24166         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24167     },
24168     // private
24169     ClientSideMediumPassword: function (pwd)
24170     {
24171         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24172     },
24173     // private
24174     ClientSideWeakPassword: function (pwd)
24175     {
24176         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24177     }
24178           
24179 })//<script type="text/javascript">
24180
24181 /*
24182  * Based  Ext JS Library 1.1.1
24183  * Copyright(c) 2006-2007, Ext JS, LLC.
24184  * LGPL
24185  *
24186  */
24187  
24188 /**
24189  * @class Roo.HtmlEditorCore
24190  * @extends Roo.Component
24191  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24192  *
24193  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24194  */
24195
24196 Roo.HtmlEditorCore = function(config){
24197     
24198     
24199     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24200     
24201     
24202     this.addEvents({
24203         /**
24204          * @event initialize
24205          * Fires when the editor is fully initialized (including the iframe)
24206          * @param {Roo.HtmlEditorCore} this
24207          */
24208         initialize: true,
24209         /**
24210          * @event activate
24211          * Fires when the editor is first receives the focus. Any insertion must wait
24212          * until after this event.
24213          * @param {Roo.HtmlEditorCore} this
24214          */
24215         activate: true,
24216          /**
24217          * @event beforesync
24218          * Fires before the textarea is updated with content from the editor iframe. Return false
24219          * to cancel the sync.
24220          * @param {Roo.HtmlEditorCore} this
24221          * @param {String} html
24222          */
24223         beforesync: true,
24224          /**
24225          * @event beforepush
24226          * Fires before the iframe editor is updated with content from the textarea. Return false
24227          * to cancel the push.
24228          * @param {Roo.HtmlEditorCore} this
24229          * @param {String} html
24230          */
24231         beforepush: true,
24232          /**
24233          * @event sync
24234          * Fires when the textarea is updated with content from the editor iframe.
24235          * @param {Roo.HtmlEditorCore} this
24236          * @param {String} html
24237          */
24238         sync: true,
24239          /**
24240          * @event push
24241          * Fires when the iframe editor is updated with content from the textarea.
24242          * @param {Roo.HtmlEditorCore} this
24243          * @param {String} html
24244          */
24245         push: true,
24246         
24247         /**
24248          * @event editorevent
24249          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24250          * @param {Roo.HtmlEditorCore} this
24251          */
24252         editorevent: true
24253         
24254     });
24255     
24256     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24257     
24258     // defaults : white / black...
24259     this.applyBlacklists();
24260     
24261     
24262     
24263 };
24264
24265
24266 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24267
24268
24269      /**
24270      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24271      */
24272     
24273     owner : false,
24274     
24275      /**
24276      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24277      *                        Roo.resizable.
24278      */
24279     resizable : false,
24280      /**
24281      * @cfg {Number} height (in pixels)
24282      */   
24283     height: 300,
24284    /**
24285      * @cfg {Number} width (in pixels)
24286      */   
24287     width: 500,
24288     
24289     /**
24290      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24291      * 
24292      */
24293     stylesheets: false,
24294     
24295     // id of frame..
24296     frameId: false,
24297     
24298     // private properties
24299     validationEvent : false,
24300     deferHeight: true,
24301     initialized : false,
24302     activated : false,
24303     sourceEditMode : false,
24304     onFocus : Roo.emptyFn,
24305     iframePad:3,
24306     hideMode:'offsets',
24307     
24308     clearUp: true,
24309     
24310     // blacklist + whitelisted elements..
24311     black: false,
24312     white: false,
24313      
24314     bodyCls : '',
24315
24316     /**
24317      * Protected method that will not generally be called directly. It
24318      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24319      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24320      */
24321     getDocMarkup : function(){
24322         // body styles..
24323         var st = '';
24324         
24325         // inherit styels from page...?? 
24326         if (this.stylesheets === false) {
24327             
24328             Roo.get(document.head).select('style').each(function(node) {
24329                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24330             });
24331             
24332             Roo.get(document.head).select('link').each(function(node) { 
24333                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24334             });
24335             
24336         } else if (!this.stylesheets.length) {
24337                 // simple..
24338                 st = '<style type="text/css">' +
24339                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24340                    '</style>';
24341         } else {
24342             for (var i in this.stylesheets) { 
24343                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24344             }
24345             
24346         }
24347         
24348         st +=  '<style type="text/css">' +
24349             'IMG { cursor: pointer } ' +
24350         '</style>';
24351
24352         var cls = 'roo-htmleditor-body';
24353         
24354         if(this.bodyCls.length){
24355             cls += ' ' + this.bodyCls;
24356         }
24357         
24358         return '<html><head>' + st  +
24359             //<style type="text/css">' +
24360             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24361             //'</style>' +
24362             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24363     },
24364
24365     // private
24366     onRender : function(ct, position)
24367     {
24368         var _t = this;
24369         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24370         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24371         
24372         
24373         this.el.dom.style.border = '0 none';
24374         this.el.dom.setAttribute('tabIndex', -1);
24375         this.el.addClass('x-hidden hide');
24376         
24377         
24378         
24379         if(Roo.isIE){ // fix IE 1px bogus margin
24380             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24381         }
24382        
24383         
24384         this.frameId = Roo.id();
24385         
24386          
24387         
24388         var iframe = this.owner.wrap.createChild({
24389             tag: 'iframe',
24390             cls: 'form-control', // bootstrap..
24391             id: this.frameId,
24392             name: this.frameId,
24393             frameBorder : 'no',
24394             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24395         }, this.el
24396         );
24397         
24398         
24399         this.iframe = iframe.dom;
24400
24401          this.assignDocWin();
24402         
24403         this.doc.designMode = 'on';
24404        
24405         this.doc.open();
24406         this.doc.write(this.getDocMarkup());
24407         this.doc.close();
24408
24409         
24410         var task = { // must defer to wait for browser to be ready
24411             run : function(){
24412                 //console.log("run task?" + this.doc.readyState);
24413                 this.assignDocWin();
24414                 if(this.doc.body || this.doc.readyState == 'complete'){
24415                     try {
24416                         this.doc.designMode="on";
24417                     } catch (e) {
24418                         return;
24419                     }
24420                     Roo.TaskMgr.stop(task);
24421                     this.initEditor.defer(10, this);
24422                 }
24423             },
24424             interval : 10,
24425             duration: 10000,
24426             scope: this
24427         };
24428         Roo.TaskMgr.start(task);
24429
24430     },
24431
24432     // private
24433     onResize : function(w, h)
24434     {
24435          Roo.log('resize: ' +w + ',' + h );
24436         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24437         if(!this.iframe){
24438             return;
24439         }
24440         if(typeof w == 'number'){
24441             
24442             this.iframe.style.width = w + 'px';
24443         }
24444         if(typeof h == 'number'){
24445             
24446             this.iframe.style.height = h + 'px';
24447             if(this.doc){
24448                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24449             }
24450         }
24451         
24452     },
24453
24454     /**
24455      * Toggles the editor between standard and source edit mode.
24456      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24457      */
24458     toggleSourceEdit : function(sourceEditMode){
24459         
24460         this.sourceEditMode = sourceEditMode === true;
24461         
24462         if(this.sourceEditMode){
24463  
24464             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24465             
24466         }else{
24467             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24468             //this.iframe.className = '';
24469             this.deferFocus();
24470         }
24471         //this.setSize(this.owner.wrap.getSize());
24472         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24473     },
24474
24475     
24476   
24477
24478     /**
24479      * Protected method that will not generally be called directly. If you need/want
24480      * custom HTML cleanup, this is the method you should override.
24481      * @param {String} html The HTML to be cleaned
24482      * return {String} The cleaned HTML
24483      */
24484     cleanHtml : function(html){
24485         html = String(html);
24486         if(html.length > 5){
24487             if(Roo.isSafari){ // strip safari nonsense
24488                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24489             }
24490         }
24491         if(html == '&nbsp;'){
24492             html = '';
24493         }
24494         return html;
24495     },
24496
24497     /**
24498      * HTML Editor -> Textarea
24499      * Protected method that will not generally be called directly. Syncs the contents
24500      * of the editor iframe with the textarea.
24501      */
24502     syncValue : function(){
24503         if(this.initialized){
24504             var bd = (this.doc.body || this.doc.documentElement);
24505             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24506             var html = bd.innerHTML;
24507             if(Roo.isSafari){
24508                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24509                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24510                 if(m && m[1]){
24511                     html = '<div style="'+m[0]+'">' + html + '</div>';
24512                 }
24513             }
24514             html = this.cleanHtml(html);
24515             // fix up the special chars.. normaly like back quotes in word...
24516             // however we do not want to do this with chinese..
24517             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24518                 
24519                 var cc = match.charCodeAt();
24520
24521                 // Get the character value, handling surrogate pairs
24522                 if (match.length == 2) {
24523                     // It's a surrogate pair, calculate the Unicode code point
24524                     var high = match.charCodeAt(0) - 0xD800;
24525                     var low  = match.charCodeAt(1) - 0xDC00;
24526                     cc = (high * 0x400) + low + 0x10000;
24527                 }  else if (
24528                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24529                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24530                     (cc >= 0xf900 && cc < 0xfb00 )
24531                 ) {
24532                         return match;
24533                 }  
24534          
24535                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24536                 return "&#" + cc + ";";
24537                 
24538                 
24539             });
24540             
24541             
24542              
24543             if(this.owner.fireEvent('beforesync', this, html) !== false){
24544                 this.el.dom.value = html;
24545                 this.owner.fireEvent('sync', this, html);
24546             }
24547         }
24548     },
24549
24550     /**
24551      * Protected method that will not generally be called directly. Pushes the value of the textarea
24552      * into the iframe editor.
24553      */
24554     pushValue : function(){
24555         if(this.initialized){
24556             var v = this.el.dom.value.trim();
24557             
24558 //            if(v.length < 1){
24559 //                v = '&#160;';
24560 //            }
24561             
24562             if(this.owner.fireEvent('beforepush', this, v) !== false){
24563                 var d = (this.doc.body || this.doc.documentElement);
24564                 d.innerHTML = v;
24565                 this.cleanUpPaste();
24566                 this.el.dom.value = d.innerHTML;
24567                 this.owner.fireEvent('push', this, v);
24568             }
24569         }
24570     },
24571
24572     // private
24573     deferFocus : function(){
24574         this.focus.defer(10, this);
24575     },
24576
24577     // doc'ed in Field
24578     focus : function(){
24579         if(this.win && !this.sourceEditMode){
24580             this.win.focus();
24581         }else{
24582             this.el.focus();
24583         }
24584     },
24585     
24586     assignDocWin: function()
24587     {
24588         var iframe = this.iframe;
24589         
24590          if(Roo.isIE){
24591             this.doc = iframe.contentWindow.document;
24592             this.win = iframe.contentWindow;
24593         } else {
24594 //            if (!Roo.get(this.frameId)) {
24595 //                return;
24596 //            }
24597 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24598 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24599             
24600             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24601                 return;
24602             }
24603             
24604             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24605             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24606         }
24607     },
24608     
24609     // private
24610     initEditor : function(){
24611         //console.log("INIT EDITOR");
24612         this.assignDocWin();
24613         
24614         
24615         
24616         this.doc.designMode="on";
24617         this.doc.open();
24618         this.doc.write(this.getDocMarkup());
24619         this.doc.close();
24620         
24621         var dbody = (this.doc.body || this.doc.documentElement);
24622         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24623         // this copies styles from the containing element into thsi one..
24624         // not sure why we need all of this..
24625         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24626         
24627         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24628         //ss['background-attachment'] = 'fixed'; // w3c
24629         dbody.bgProperties = 'fixed'; // ie
24630         //Roo.DomHelper.applyStyles(dbody, ss);
24631         Roo.EventManager.on(this.doc, {
24632             //'mousedown': this.onEditorEvent,
24633             'mouseup': this.onEditorEvent,
24634             'dblclick': this.onEditorEvent,
24635             'click': this.onEditorEvent,
24636             'keyup': this.onEditorEvent,
24637             buffer:100,
24638             scope: this
24639         });
24640         if(Roo.isGecko){
24641             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24642         }
24643         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24644             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24645         }
24646         this.initialized = true;
24647
24648         this.owner.fireEvent('initialize', this);
24649         this.pushValue();
24650     },
24651
24652     // private
24653     onDestroy : function(){
24654         
24655         
24656         
24657         if(this.rendered){
24658             
24659             //for (var i =0; i < this.toolbars.length;i++) {
24660             //    // fixme - ask toolbars for heights?
24661             //    this.toolbars[i].onDestroy();
24662            // }
24663             
24664             //this.wrap.dom.innerHTML = '';
24665             //this.wrap.remove();
24666         }
24667     },
24668
24669     // private
24670     onFirstFocus : function(){
24671         
24672         this.assignDocWin();
24673         
24674         
24675         this.activated = true;
24676          
24677     
24678         if(Roo.isGecko){ // prevent silly gecko errors
24679             this.win.focus();
24680             var s = this.win.getSelection();
24681             if(!s.focusNode || s.focusNode.nodeType != 3){
24682                 var r = s.getRangeAt(0);
24683                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24684                 r.collapse(true);
24685                 this.deferFocus();
24686             }
24687             try{
24688                 this.execCmd('useCSS', true);
24689                 this.execCmd('styleWithCSS', false);
24690             }catch(e){}
24691         }
24692         this.owner.fireEvent('activate', this);
24693     },
24694
24695     // private
24696     adjustFont: function(btn){
24697         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24698         //if(Roo.isSafari){ // safari
24699         //    adjust *= 2;
24700        // }
24701         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24702         if(Roo.isSafari){ // safari
24703             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24704             v =  (v < 10) ? 10 : v;
24705             v =  (v > 48) ? 48 : v;
24706             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24707             
24708         }
24709         
24710         
24711         v = Math.max(1, v+adjust);
24712         
24713         this.execCmd('FontSize', v  );
24714     },
24715
24716     onEditorEvent : function(e)
24717     {
24718         this.owner.fireEvent('editorevent', this, e);
24719       //  this.updateToolbar();
24720         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24721     },
24722
24723     insertTag : function(tg)
24724     {
24725         // could be a bit smarter... -> wrap the current selected tRoo..
24726         if (tg.toLowerCase() == 'span' ||
24727             tg.toLowerCase() == 'code' ||
24728             tg.toLowerCase() == 'sup' ||
24729             tg.toLowerCase() == 'sub' 
24730             ) {
24731             
24732             range = this.createRange(this.getSelection());
24733             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24734             wrappingNode.appendChild(range.extractContents());
24735             range.insertNode(wrappingNode);
24736
24737             return;
24738             
24739             
24740             
24741         }
24742         this.execCmd("formatblock",   tg);
24743         
24744     },
24745     
24746     insertText : function(txt)
24747     {
24748         
24749         
24750         var range = this.createRange();
24751         range.deleteContents();
24752                //alert(Sender.getAttribute('label'));
24753                
24754         range.insertNode(this.doc.createTextNode(txt));
24755     } ,
24756     
24757      
24758
24759     /**
24760      * Executes a Midas editor command on the editor document and performs necessary focus and
24761      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24762      * @param {String} cmd The Midas command
24763      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24764      */
24765     relayCmd : function(cmd, value){
24766         this.win.focus();
24767         this.execCmd(cmd, value);
24768         this.owner.fireEvent('editorevent', this);
24769         //this.updateToolbar();
24770         this.owner.deferFocus();
24771     },
24772
24773     /**
24774      * Executes a Midas editor command directly on the editor document.
24775      * For visual commands, you should use {@link #relayCmd} instead.
24776      * <b>This should only be called after the editor is initialized.</b>
24777      * @param {String} cmd The Midas command
24778      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24779      */
24780     execCmd : function(cmd, value){
24781         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24782         this.syncValue();
24783     },
24784  
24785  
24786    
24787     /**
24788      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24789      * to insert tRoo.
24790      * @param {String} text | dom node.. 
24791      */
24792     insertAtCursor : function(text)
24793     {
24794         
24795         if(!this.activated){
24796             return;
24797         }
24798         /*
24799         if(Roo.isIE){
24800             this.win.focus();
24801             var r = this.doc.selection.createRange();
24802             if(r){
24803                 r.collapse(true);
24804                 r.pasteHTML(text);
24805                 this.syncValue();
24806                 this.deferFocus();
24807             
24808             }
24809             return;
24810         }
24811         */
24812         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24813             this.win.focus();
24814             
24815             
24816             // from jquery ui (MIT licenced)
24817             var range, node;
24818             var win = this.win;
24819             
24820             if (win.getSelection && win.getSelection().getRangeAt) {
24821                 range = win.getSelection().getRangeAt(0);
24822                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24823                 range.insertNode(node);
24824             } else if (win.document.selection && win.document.selection.createRange) {
24825                 // no firefox support
24826                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24827                 win.document.selection.createRange().pasteHTML(txt);
24828             } else {
24829                 // no firefox support
24830                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24831                 this.execCmd('InsertHTML', txt);
24832             } 
24833             
24834             this.syncValue();
24835             
24836             this.deferFocus();
24837         }
24838     },
24839  // private
24840     mozKeyPress : function(e){
24841         if(e.ctrlKey){
24842             var c = e.getCharCode(), cmd;
24843           
24844             if(c > 0){
24845                 c = String.fromCharCode(c).toLowerCase();
24846                 switch(c){
24847                     case 'b':
24848                         cmd = 'bold';
24849                         break;
24850                     case 'i':
24851                         cmd = 'italic';
24852                         break;
24853                     
24854                     case 'u':
24855                         cmd = 'underline';
24856                         break;
24857                     
24858                     case 'v':
24859                         this.cleanUpPaste.defer(100, this);
24860                         return;
24861                         
24862                 }
24863                 if(cmd){
24864                     this.win.focus();
24865                     this.execCmd(cmd);
24866                     this.deferFocus();
24867                     e.preventDefault();
24868                 }
24869                 
24870             }
24871         }
24872     },
24873
24874     // private
24875     fixKeys : function(){ // load time branching for fastest keydown performance
24876         if(Roo.isIE){
24877             return function(e){
24878                 var k = e.getKey(), r;
24879                 if(k == e.TAB){
24880                     e.stopEvent();
24881                     r = this.doc.selection.createRange();
24882                     if(r){
24883                         r.collapse(true);
24884                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24885                         this.deferFocus();
24886                     }
24887                     return;
24888                 }
24889                 
24890                 if(k == e.ENTER){
24891                     r = this.doc.selection.createRange();
24892                     if(r){
24893                         var target = r.parentElement();
24894                         if(!target || target.tagName.toLowerCase() != 'li'){
24895                             e.stopEvent();
24896                             r.pasteHTML('<br />');
24897                             r.collapse(false);
24898                             r.select();
24899                         }
24900                     }
24901                 }
24902                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24903                     this.cleanUpPaste.defer(100, this);
24904                     return;
24905                 }
24906                 
24907                 
24908             };
24909         }else if(Roo.isOpera){
24910             return function(e){
24911                 var k = e.getKey();
24912                 if(k == e.TAB){
24913                     e.stopEvent();
24914                     this.win.focus();
24915                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24916                     this.deferFocus();
24917                 }
24918                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24919                     this.cleanUpPaste.defer(100, this);
24920                     return;
24921                 }
24922                 
24923             };
24924         }else if(Roo.isSafari){
24925             return function(e){
24926                 var k = e.getKey();
24927                 
24928                 if(k == e.TAB){
24929                     e.stopEvent();
24930                     this.execCmd('InsertText','\t');
24931                     this.deferFocus();
24932                     return;
24933                 }
24934                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24935                     this.cleanUpPaste.defer(100, this);
24936                     return;
24937                 }
24938                 
24939              };
24940         }
24941     }(),
24942     
24943     getAllAncestors: function()
24944     {
24945         var p = this.getSelectedNode();
24946         var a = [];
24947         if (!p) {
24948             a.push(p); // push blank onto stack..
24949             p = this.getParentElement();
24950         }
24951         
24952         
24953         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24954             a.push(p);
24955             p = p.parentNode;
24956         }
24957         a.push(this.doc.body);
24958         return a;
24959     },
24960     lastSel : false,
24961     lastSelNode : false,
24962     
24963     
24964     getSelection : function() 
24965     {
24966         this.assignDocWin();
24967         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24968     },
24969     
24970     getSelectedNode: function() 
24971     {
24972         // this may only work on Gecko!!!
24973         
24974         // should we cache this!!!!
24975         
24976         
24977         
24978          
24979         var range = this.createRange(this.getSelection()).cloneRange();
24980         
24981         if (Roo.isIE) {
24982             var parent = range.parentElement();
24983             while (true) {
24984                 var testRange = range.duplicate();
24985                 testRange.moveToElementText(parent);
24986                 if (testRange.inRange(range)) {
24987                     break;
24988                 }
24989                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24990                     break;
24991                 }
24992                 parent = parent.parentElement;
24993             }
24994             return parent;
24995         }
24996         
24997         // is ancestor a text element.
24998         var ac =  range.commonAncestorContainer;
24999         if (ac.nodeType == 3) {
25000             ac = ac.parentNode;
25001         }
25002         
25003         var ar = ac.childNodes;
25004          
25005         var nodes = [];
25006         var other_nodes = [];
25007         var has_other_nodes = false;
25008         for (var i=0;i<ar.length;i++) {
25009             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25010                 continue;
25011             }
25012             // fullly contained node.
25013             
25014             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25015                 nodes.push(ar[i]);
25016                 continue;
25017             }
25018             
25019             // probably selected..
25020             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25021                 other_nodes.push(ar[i]);
25022                 continue;
25023             }
25024             // outer..
25025             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25026                 continue;
25027             }
25028             
25029             
25030             has_other_nodes = true;
25031         }
25032         if (!nodes.length && other_nodes.length) {
25033             nodes= other_nodes;
25034         }
25035         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25036             return false;
25037         }
25038         
25039         return nodes[0];
25040     },
25041     createRange: function(sel)
25042     {
25043         // this has strange effects when using with 
25044         // top toolbar - not sure if it's a great idea.
25045         //this.editor.contentWindow.focus();
25046         if (typeof sel != "undefined") {
25047             try {
25048                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25049             } catch(e) {
25050                 return this.doc.createRange();
25051             }
25052         } else {
25053             return this.doc.createRange();
25054         }
25055     },
25056     getParentElement: function()
25057     {
25058         
25059         this.assignDocWin();
25060         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25061         
25062         var range = this.createRange(sel);
25063          
25064         try {
25065             var p = range.commonAncestorContainer;
25066             while (p.nodeType == 3) { // text node
25067                 p = p.parentNode;
25068             }
25069             return p;
25070         } catch (e) {
25071             return null;
25072         }
25073     
25074     },
25075     /***
25076      *
25077      * Range intersection.. the hard stuff...
25078      *  '-1' = before
25079      *  '0' = hits..
25080      *  '1' = after.
25081      *         [ -- selected range --- ]
25082      *   [fail]                        [fail]
25083      *
25084      *    basically..
25085      *      if end is before start or  hits it. fail.
25086      *      if start is after end or hits it fail.
25087      *
25088      *   if either hits (but other is outside. - then it's not 
25089      *   
25090      *    
25091      **/
25092     
25093     
25094     // @see http://www.thismuchiknow.co.uk/?p=64.
25095     rangeIntersectsNode : function(range, node)
25096     {
25097         var nodeRange = node.ownerDocument.createRange();
25098         try {
25099             nodeRange.selectNode(node);
25100         } catch (e) {
25101             nodeRange.selectNodeContents(node);
25102         }
25103     
25104         var rangeStartRange = range.cloneRange();
25105         rangeStartRange.collapse(true);
25106     
25107         var rangeEndRange = range.cloneRange();
25108         rangeEndRange.collapse(false);
25109     
25110         var nodeStartRange = nodeRange.cloneRange();
25111         nodeStartRange.collapse(true);
25112     
25113         var nodeEndRange = nodeRange.cloneRange();
25114         nodeEndRange.collapse(false);
25115     
25116         return rangeStartRange.compareBoundaryPoints(
25117                  Range.START_TO_START, nodeEndRange) == -1 &&
25118                rangeEndRange.compareBoundaryPoints(
25119                  Range.START_TO_START, nodeStartRange) == 1;
25120         
25121          
25122     },
25123     rangeCompareNode : function(range, node)
25124     {
25125         var nodeRange = node.ownerDocument.createRange();
25126         try {
25127             nodeRange.selectNode(node);
25128         } catch (e) {
25129             nodeRange.selectNodeContents(node);
25130         }
25131         
25132         
25133         range.collapse(true);
25134     
25135         nodeRange.collapse(true);
25136      
25137         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25138         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25139          
25140         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25141         
25142         var nodeIsBefore   =  ss == 1;
25143         var nodeIsAfter    = ee == -1;
25144         
25145         if (nodeIsBefore && nodeIsAfter) {
25146             return 0; // outer
25147         }
25148         if (!nodeIsBefore && nodeIsAfter) {
25149             return 1; //right trailed.
25150         }
25151         
25152         if (nodeIsBefore && !nodeIsAfter) {
25153             return 2;  // left trailed.
25154         }
25155         // fully contined.
25156         return 3;
25157     },
25158
25159     // private? - in a new class?
25160     cleanUpPaste :  function()
25161     {
25162         // cleans up the whole document..
25163         Roo.log('cleanuppaste');
25164         
25165         this.cleanUpChildren(this.doc.body);
25166         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25167         if (clean != this.doc.body.innerHTML) {
25168             this.doc.body.innerHTML = clean;
25169         }
25170         
25171     },
25172     
25173     cleanWordChars : function(input) {// change the chars to hex code
25174         var he = Roo.HtmlEditorCore;
25175         
25176         var output = input;
25177         Roo.each(he.swapCodes, function(sw) { 
25178             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25179             
25180             output = output.replace(swapper, sw[1]);
25181         });
25182         
25183         return output;
25184     },
25185     
25186     
25187     cleanUpChildren : function (n)
25188     {
25189         if (!n.childNodes.length) {
25190             return;
25191         }
25192         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25193            this.cleanUpChild(n.childNodes[i]);
25194         }
25195     },
25196     
25197     
25198         
25199     
25200     cleanUpChild : function (node)
25201     {
25202         var ed = this;
25203         //console.log(node);
25204         if (node.nodeName == "#text") {
25205             // clean up silly Windows -- stuff?
25206             return; 
25207         }
25208         if (node.nodeName == "#comment") {
25209             node.parentNode.removeChild(node);
25210             // clean up silly Windows -- stuff?
25211             return; 
25212         }
25213         var lcname = node.tagName.toLowerCase();
25214         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25215         // whitelist of tags..
25216         
25217         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25218             // remove node.
25219             node.parentNode.removeChild(node);
25220             return;
25221             
25222         }
25223         
25224         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25225         
25226         // spans with no attributes - just remove them..
25227         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25228             remove_keep_children = true;
25229         }
25230         
25231         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25232         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25233         
25234         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25235         //    remove_keep_children = true;
25236         //}
25237         
25238         if (remove_keep_children) {
25239             this.cleanUpChildren(node);
25240             // inserts everything just before this node...
25241             while (node.childNodes.length) {
25242                 var cn = node.childNodes[0];
25243                 node.removeChild(cn);
25244                 node.parentNode.insertBefore(cn, node);
25245             }
25246             node.parentNode.removeChild(node);
25247             return;
25248         }
25249         
25250         if (!node.attributes || !node.attributes.length) {
25251             
25252           
25253             
25254             
25255             this.cleanUpChildren(node);
25256             return;
25257         }
25258         
25259         function cleanAttr(n,v)
25260         {
25261             
25262             if (v.match(/^\./) || v.match(/^\//)) {
25263                 return;
25264             }
25265             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25266                 return;
25267             }
25268             if (v.match(/^#/)) {
25269                 return;
25270             }
25271             if (v.match(/^\{/)) { // allow template editing.
25272                 return;
25273             }
25274 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25275             node.removeAttribute(n);
25276             
25277         }
25278         
25279         var cwhite = this.cwhite;
25280         var cblack = this.cblack;
25281             
25282         function cleanStyle(n,v)
25283         {
25284             if (v.match(/expression/)) { //XSS?? should we even bother..
25285                 node.removeAttribute(n);
25286                 return;
25287             }
25288             
25289             var parts = v.split(/;/);
25290             var clean = [];
25291             
25292             Roo.each(parts, function(p) {
25293                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25294                 if (!p.length) {
25295                     return true;
25296                 }
25297                 var l = p.split(':').shift().replace(/\s+/g,'');
25298                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25299                 
25300                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25301 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25302                     //node.removeAttribute(n);
25303                     return true;
25304                 }
25305                 //Roo.log()
25306                 // only allow 'c whitelisted system attributes'
25307                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25308 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25309                     //node.removeAttribute(n);
25310                     return true;
25311                 }
25312                 
25313                 
25314                  
25315                 
25316                 clean.push(p);
25317                 return true;
25318             });
25319             if (clean.length) { 
25320                 node.setAttribute(n, clean.join(';'));
25321             } else {
25322                 node.removeAttribute(n);
25323             }
25324             
25325         }
25326         
25327         
25328         for (var i = node.attributes.length-1; i > -1 ; i--) {
25329             var a = node.attributes[i];
25330             //console.log(a);
25331             
25332             if (a.name.toLowerCase().substr(0,2)=='on')  {
25333                 node.removeAttribute(a.name);
25334                 continue;
25335             }
25336             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25337                 node.removeAttribute(a.name);
25338                 continue;
25339             }
25340             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25341                 cleanAttr(a.name,a.value); // fixme..
25342                 continue;
25343             }
25344             if (a.name == 'style') {
25345                 cleanStyle(a.name,a.value);
25346                 continue;
25347             }
25348             /// clean up MS crap..
25349             // tecnically this should be a list of valid class'es..
25350             
25351             
25352             if (a.name == 'class') {
25353                 if (a.value.match(/^Mso/)) {
25354                     node.removeAttribute('class');
25355                 }
25356                 
25357                 if (a.value.match(/^body$/)) {
25358                     node.removeAttribute('class');
25359                 }
25360                 continue;
25361             }
25362             
25363             // style cleanup!?
25364             // class cleanup?
25365             
25366         }
25367         
25368         
25369         this.cleanUpChildren(node);
25370         
25371         
25372     },
25373     
25374     /**
25375      * Clean up MS wordisms...
25376      */
25377     cleanWord : function(node)
25378     {
25379         if (!node) {
25380             this.cleanWord(this.doc.body);
25381             return;
25382         }
25383         
25384         if(
25385                 node.nodeName == 'SPAN' &&
25386                 !node.hasAttributes() &&
25387                 node.childNodes.length == 1 &&
25388                 node.firstChild.nodeName == "#text"  
25389         ) {
25390             var textNode = node.firstChild;
25391             node.removeChild(textNode);
25392             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25393                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25394             }
25395             node.parentNode.insertBefore(textNode, node);
25396             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25397                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25398             }
25399             node.parentNode.removeChild(node);
25400         }
25401         
25402         if (node.nodeName == "#text") {
25403             // clean up silly Windows -- stuff?
25404             return; 
25405         }
25406         if (node.nodeName == "#comment") {
25407             node.parentNode.removeChild(node);
25408             // clean up silly Windows -- stuff?
25409             return; 
25410         }
25411         
25412         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25413             node.parentNode.removeChild(node);
25414             return;
25415         }
25416         //Roo.log(node.tagName);
25417         // remove - but keep children..
25418         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25419             //Roo.log('-- removed');
25420             while (node.childNodes.length) {
25421                 var cn = node.childNodes[0];
25422                 node.removeChild(cn);
25423                 node.parentNode.insertBefore(cn, node);
25424                 // move node to parent - and clean it..
25425                 this.cleanWord(cn);
25426             }
25427             node.parentNode.removeChild(node);
25428             /// no need to iterate chidlren = it's got none..
25429             //this.iterateChildren(node, this.cleanWord);
25430             return;
25431         }
25432         // clean styles
25433         if (node.className.length) {
25434             
25435             var cn = node.className.split(/\W+/);
25436             var cna = [];
25437             Roo.each(cn, function(cls) {
25438                 if (cls.match(/Mso[a-zA-Z]+/)) {
25439                     return;
25440                 }
25441                 cna.push(cls);
25442             });
25443             node.className = cna.length ? cna.join(' ') : '';
25444             if (!cna.length) {
25445                 node.removeAttribute("class");
25446             }
25447         }
25448         
25449         if (node.hasAttribute("lang")) {
25450             node.removeAttribute("lang");
25451         }
25452         
25453         if (node.hasAttribute("style")) {
25454             
25455             var styles = node.getAttribute("style").split(";");
25456             var nstyle = [];
25457             Roo.each(styles, function(s) {
25458                 if (!s.match(/:/)) {
25459                     return;
25460                 }
25461                 var kv = s.split(":");
25462                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25463                     return;
25464                 }
25465                 // what ever is left... we allow.
25466                 nstyle.push(s);
25467             });
25468             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25469             if (!nstyle.length) {
25470                 node.removeAttribute('style');
25471             }
25472         }
25473         this.iterateChildren(node, this.cleanWord);
25474         
25475         
25476         
25477     },
25478     /**
25479      * iterateChildren of a Node, calling fn each time, using this as the scole..
25480      * @param {DomNode} node node to iterate children of.
25481      * @param {Function} fn method of this class to call on each item.
25482      */
25483     iterateChildren : function(node, fn)
25484     {
25485         if (!node.childNodes.length) {
25486                 return;
25487         }
25488         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25489            fn.call(this, node.childNodes[i])
25490         }
25491     },
25492     
25493     
25494     /**
25495      * cleanTableWidths.
25496      *
25497      * Quite often pasting from word etc.. results in tables with column and widths.
25498      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25499      *
25500      */
25501     cleanTableWidths : function(node)
25502     {
25503          
25504          
25505         if (!node) {
25506             this.cleanTableWidths(this.doc.body);
25507             return;
25508         }
25509         
25510         // ignore list...
25511         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25512             return; 
25513         }
25514         Roo.log(node.tagName);
25515         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25516             this.iterateChildren(node, this.cleanTableWidths);
25517             return;
25518         }
25519         if (node.hasAttribute('width')) {
25520             node.removeAttribute('width');
25521         }
25522         
25523          
25524         if (node.hasAttribute("style")) {
25525             // pretty basic...
25526             
25527             var styles = node.getAttribute("style").split(";");
25528             var nstyle = [];
25529             Roo.each(styles, function(s) {
25530                 if (!s.match(/:/)) {
25531                     return;
25532                 }
25533                 var kv = s.split(":");
25534                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25535                     return;
25536                 }
25537                 // what ever is left... we allow.
25538                 nstyle.push(s);
25539             });
25540             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25541             if (!nstyle.length) {
25542                 node.removeAttribute('style');
25543             }
25544         }
25545         
25546         this.iterateChildren(node, this.cleanTableWidths);
25547         
25548         
25549     },
25550     
25551     
25552     
25553     
25554     domToHTML : function(currentElement, depth, nopadtext) {
25555         
25556         depth = depth || 0;
25557         nopadtext = nopadtext || false;
25558     
25559         if (!currentElement) {
25560             return this.domToHTML(this.doc.body);
25561         }
25562         
25563         //Roo.log(currentElement);
25564         var j;
25565         var allText = false;
25566         var nodeName = currentElement.nodeName;
25567         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25568         
25569         if  (nodeName == '#text') {
25570             
25571             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25572         }
25573         
25574         
25575         var ret = '';
25576         if (nodeName != 'BODY') {
25577              
25578             var i = 0;
25579             // Prints the node tagName, such as <A>, <IMG>, etc
25580             if (tagName) {
25581                 var attr = [];
25582                 for(i = 0; i < currentElement.attributes.length;i++) {
25583                     // quoting?
25584                     var aname = currentElement.attributes.item(i).name;
25585                     if (!currentElement.attributes.item(i).value.length) {
25586                         continue;
25587                     }
25588                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25589                 }
25590                 
25591                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25592             } 
25593             else {
25594                 
25595                 // eack
25596             }
25597         } else {
25598             tagName = false;
25599         }
25600         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25601             return ret;
25602         }
25603         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25604             nopadtext = true;
25605         }
25606         
25607         
25608         // Traverse the tree
25609         i = 0;
25610         var currentElementChild = currentElement.childNodes.item(i);
25611         var allText = true;
25612         var innerHTML  = '';
25613         lastnode = '';
25614         while (currentElementChild) {
25615             // Formatting code (indent the tree so it looks nice on the screen)
25616             var nopad = nopadtext;
25617             if (lastnode == 'SPAN') {
25618                 nopad  = true;
25619             }
25620             // text
25621             if  (currentElementChild.nodeName == '#text') {
25622                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25623                 toadd = nopadtext ? toadd : toadd.trim();
25624                 if (!nopad && toadd.length > 80) {
25625                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25626                 }
25627                 innerHTML  += toadd;
25628                 
25629                 i++;
25630                 currentElementChild = currentElement.childNodes.item(i);
25631                 lastNode = '';
25632                 continue;
25633             }
25634             allText = false;
25635             
25636             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25637                 
25638             // Recursively traverse the tree structure of the child node
25639             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25640             lastnode = currentElementChild.nodeName;
25641             i++;
25642             currentElementChild=currentElement.childNodes.item(i);
25643         }
25644         
25645         ret += innerHTML;
25646         
25647         if (!allText) {
25648                 // The remaining code is mostly for formatting the tree
25649             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25650         }
25651         
25652         
25653         if (tagName) {
25654             ret+= "</"+tagName+">";
25655         }
25656         return ret;
25657         
25658     },
25659         
25660     applyBlacklists : function()
25661     {
25662         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25663         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25664         
25665         this.white = [];
25666         this.black = [];
25667         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25668             if (b.indexOf(tag) > -1) {
25669                 return;
25670             }
25671             this.white.push(tag);
25672             
25673         }, this);
25674         
25675         Roo.each(w, function(tag) {
25676             if (b.indexOf(tag) > -1) {
25677                 return;
25678             }
25679             if (this.white.indexOf(tag) > -1) {
25680                 return;
25681             }
25682             this.white.push(tag);
25683             
25684         }, this);
25685         
25686         
25687         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25688             if (w.indexOf(tag) > -1) {
25689                 return;
25690             }
25691             this.black.push(tag);
25692             
25693         }, this);
25694         
25695         Roo.each(b, function(tag) {
25696             if (w.indexOf(tag) > -1) {
25697                 return;
25698             }
25699             if (this.black.indexOf(tag) > -1) {
25700                 return;
25701             }
25702             this.black.push(tag);
25703             
25704         }, this);
25705         
25706         
25707         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25708         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25709         
25710         this.cwhite = [];
25711         this.cblack = [];
25712         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25713             if (b.indexOf(tag) > -1) {
25714                 return;
25715             }
25716             this.cwhite.push(tag);
25717             
25718         }, this);
25719         
25720         Roo.each(w, function(tag) {
25721             if (b.indexOf(tag) > -1) {
25722                 return;
25723             }
25724             if (this.cwhite.indexOf(tag) > -1) {
25725                 return;
25726             }
25727             this.cwhite.push(tag);
25728             
25729         }, this);
25730         
25731         
25732         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25733             if (w.indexOf(tag) > -1) {
25734                 return;
25735             }
25736             this.cblack.push(tag);
25737             
25738         }, this);
25739         
25740         Roo.each(b, function(tag) {
25741             if (w.indexOf(tag) > -1) {
25742                 return;
25743             }
25744             if (this.cblack.indexOf(tag) > -1) {
25745                 return;
25746             }
25747             this.cblack.push(tag);
25748             
25749         }, this);
25750     },
25751     
25752     setStylesheets : function(stylesheets)
25753     {
25754         if(typeof(stylesheets) == 'string'){
25755             Roo.get(this.iframe.contentDocument.head).createChild({
25756                 tag : 'link',
25757                 rel : 'stylesheet',
25758                 type : 'text/css',
25759                 href : stylesheets
25760             });
25761             
25762             return;
25763         }
25764         var _this = this;
25765      
25766         Roo.each(stylesheets, function(s) {
25767             if(!s.length){
25768                 return;
25769             }
25770             
25771             Roo.get(_this.iframe.contentDocument.head).createChild({
25772                 tag : 'link',
25773                 rel : 'stylesheet',
25774                 type : 'text/css',
25775                 href : s
25776             });
25777         });
25778
25779         
25780     },
25781     
25782     removeStylesheets : function()
25783     {
25784         var _this = this;
25785         
25786         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25787             s.remove();
25788         });
25789     },
25790     
25791     setStyle : function(style)
25792     {
25793         Roo.get(this.iframe.contentDocument.head).createChild({
25794             tag : 'style',
25795             type : 'text/css',
25796             html : style
25797         });
25798
25799         return;
25800     }
25801     
25802     // hide stuff that is not compatible
25803     /**
25804      * @event blur
25805      * @hide
25806      */
25807     /**
25808      * @event change
25809      * @hide
25810      */
25811     /**
25812      * @event focus
25813      * @hide
25814      */
25815     /**
25816      * @event specialkey
25817      * @hide
25818      */
25819     /**
25820      * @cfg {String} fieldClass @hide
25821      */
25822     /**
25823      * @cfg {String} focusClass @hide
25824      */
25825     /**
25826      * @cfg {String} autoCreate @hide
25827      */
25828     /**
25829      * @cfg {String} inputType @hide
25830      */
25831     /**
25832      * @cfg {String} invalidClass @hide
25833      */
25834     /**
25835      * @cfg {String} invalidText @hide
25836      */
25837     /**
25838      * @cfg {String} msgFx @hide
25839      */
25840     /**
25841      * @cfg {String} validateOnBlur @hide
25842      */
25843 });
25844
25845 Roo.HtmlEditorCore.white = [
25846         'area', 'br', 'img', 'input', 'hr', 'wbr',
25847         
25848        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25849        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25850        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25851        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25852        'table',   'ul',         'xmp', 
25853        
25854        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25855       'thead',   'tr', 
25856      
25857       'dir', 'menu', 'ol', 'ul', 'dl',
25858        
25859       'embed',  'object'
25860 ];
25861
25862
25863 Roo.HtmlEditorCore.black = [
25864     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25865         'applet', // 
25866         'base',   'basefont', 'bgsound', 'blink',  'body', 
25867         'frame',  'frameset', 'head',    'html',   'ilayer', 
25868         'iframe', 'layer',  'link',     'meta',    'object',   
25869         'script', 'style' ,'title',  'xml' // clean later..
25870 ];
25871 Roo.HtmlEditorCore.clean = [
25872     'script', 'style', 'title', 'xml'
25873 ];
25874 Roo.HtmlEditorCore.remove = [
25875     'font'
25876 ];
25877 // attributes..
25878
25879 Roo.HtmlEditorCore.ablack = [
25880     'on'
25881 ];
25882     
25883 Roo.HtmlEditorCore.aclean = [ 
25884     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25885 ];
25886
25887 // protocols..
25888 Roo.HtmlEditorCore.pwhite= [
25889         'http',  'https',  'mailto'
25890 ];
25891
25892 // white listed style attributes.
25893 Roo.HtmlEditorCore.cwhite= [
25894       //  'text-align', /// default is to allow most things..
25895       
25896          
25897 //        'font-size'//??
25898 ];
25899
25900 // black listed style attributes.
25901 Roo.HtmlEditorCore.cblack= [
25902       //  'font-size' -- this can be set by the project 
25903 ];
25904
25905
25906 Roo.HtmlEditorCore.swapCodes   =[ 
25907     [    8211, "&#8211;" ], 
25908     [    8212, "&#8212;" ], 
25909     [    8216,  "'" ],  
25910     [    8217, "'" ],  
25911     [    8220, '"' ],  
25912     [    8221, '"' ],  
25913     [    8226, "*" ],  
25914     [    8230, "..." ]
25915 ]; 
25916
25917     /*
25918  * - LGPL
25919  *
25920  * HtmlEditor
25921  * 
25922  */
25923
25924 /**
25925  * @class Roo.bootstrap.HtmlEditor
25926  * @extends Roo.bootstrap.TextArea
25927  * Bootstrap HtmlEditor class
25928
25929  * @constructor
25930  * Create a new HtmlEditor
25931  * @param {Object} config The config object
25932  */
25933
25934 Roo.bootstrap.HtmlEditor = function(config){
25935     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25936     if (!this.toolbars) {
25937         this.toolbars = [];
25938     }
25939     
25940     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25941     this.addEvents({
25942             /**
25943              * @event initialize
25944              * Fires when the editor is fully initialized (including the iframe)
25945              * @param {HtmlEditor} this
25946              */
25947             initialize: true,
25948             /**
25949              * @event activate
25950              * Fires when the editor is first receives the focus. Any insertion must wait
25951              * until after this event.
25952              * @param {HtmlEditor} this
25953              */
25954             activate: true,
25955              /**
25956              * @event beforesync
25957              * Fires before the textarea is updated with content from the editor iframe. Return false
25958              * to cancel the sync.
25959              * @param {HtmlEditor} this
25960              * @param {String} html
25961              */
25962             beforesync: true,
25963              /**
25964              * @event beforepush
25965              * Fires before the iframe editor is updated with content from the textarea. Return false
25966              * to cancel the push.
25967              * @param {HtmlEditor} this
25968              * @param {String} html
25969              */
25970             beforepush: true,
25971              /**
25972              * @event sync
25973              * Fires when the textarea is updated with content from the editor iframe.
25974              * @param {HtmlEditor} this
25975              * @param {String} html
25976              */
25977             sync: true,
25978              /**
25979              * @event push
25980              * Fires when the iframe editor is updated with content from the textarea.
25981              * @param {HtmlEditor} this
25982              * @param {String} html
25983              */
25984             push: true,
25985              /**
25986              * @event editmodechange
25987              * Fires when the editor switches edit modes
25988              * @param {HtmlEditor} this
25989              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25990              */
25991             editmodechange: true,
25992             /**
25993              * @event editorevent
25994              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25995              * @param {HtmlEditor} this
25996              */
25997             editorevent: true,
25998             /**
25999              * @event firstfocus
26000              * Fires when on first focus - needed by toolbars..
26001              * @param {HtmlEditor} this
26002              */
26003             firstfocus: true,
26004             /**
26005              * @event autosave
26006              * Auto save the htmlEditor value as a file into Events
26007              * @param {HtmlEditor} this
26008              */
26009             autosave: true,
26010             /**
26011              * @event savedpreview
26012              * preview the saved version of htmlEditor
26013              * @param {HtmlEditor} this
26014              */
26015             savedpreview: true
26016         });
26017 };
26018
26019
26020 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
26021     
26022     
26023       /**
26024      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26025      */
26026     toolbars : false,
26027     
26028      /**
26029     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26030     */
26031     btns : [],
26032    
26033      /**
26034      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26035      *                        Roo.resizable.
26036      */
26037     resizable : false,
26038      /**
26039      * @cfg {Number} height (in pixels)
26040      */   
26041     height: 300,
26042    /**
26043      * @cfg {Number} width (in pixels)
26044      */   
26045     width: false,
26046     
26047     /**
26048      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26049      * 
26050      */
26051     stylesheets: false,
26052     
26053     // id of frame..
26054     frameId: false,
26055     
26056     // private properties
26057     validationEvent : false,
26058     deferHeight: true,
26059     initialized : false,
26060     activated : false,
26061     
26062     onFocus : Roo.emptyFn,
26063     iframePad:3,
26064     hideMode:'offsets',
26065     
26066     tbContainer : false,
26067     
26068     bodyCls : '',
26069     
26070     toolbarContainer :function() {
26071         return this.wrap.select('.x-html-editor-tb',true).first();
26072     },
26073
26074     /**
26075      * Protected method that will not generally be called directly. It
26076      * is called when the editor creates its toolbar. Override this method if you need to
26077      * add custom toolbar buttons.
26078      * @param {HtmlEditor} editor
26079      */
26080     createToolbar : function(){
26081         Roo.log('renewing');
26082         Roo.log("create toolbars");
26083         
26084         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26085         this.toolbars[0].render(this.toolbarContainer());
26086         
26087         return;
26088         
26089 //        if (!editor.toolbars || !editor.toolbars.length) {
26090 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26091 //        }
26092 //        
26093 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26094 //            editor.toolbars[i] = Roo.factory(
26095 //                    typeof(editor.toolbars[i]) == 'string' ?
26096 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26097 //                Roo.bootstrap.HtmlEditor);
26098 //            editor.toolbars[i].init(editor);
26099 //        }
26100     },
26101
26102      
26103     // private
26104     onRender : function(ct, position)
26105     {
26106        // Roo.log("Call onRender: " + this.xtype);
26107         var _t = this;
26108         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26109       
26110         this.wrap = this.inputEl().wrap({
26111             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26112         });
26113         
26114         this.editorcore.onRender(ct, position);
26115          
26116         if (this.resizable) {
26117             this.resizeEl = new Roo.Resizable(this.wrap, {
26118                 pinned : true,
26119                 wrap: true,
26120                 dynamic : true,
26121                 minHeight : this.height,
26122                 height: this.height,
26123                 handles : this.resizable,
26124                 width: this.width,
26125                 listeners : {
26126                     resize : function(r, w, h) {
26127                         _t.onResize(w,h); // -something
26128                     }
26129                 }
26130             });
26131             
26132         }
26133         this.createToolbar(this);
26134        
26135         
26136         if(!this.width && this.resizable){
26137             this.setSize(this.wrap.getSize());
26138         }
26139         if (this.resizeEl) {
26140             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26141             // should trigger onReize..
26142         }
26143         
26144     },
26145
26146     // private
26147     onResize : function(w, h)
26148     {
26149         Roo.log('resize: ' +w + ',' + h );
26150         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26151         var ew = false;
26152         var eh = false;
26153         
26154         if(this.inputEl() ){
26155             if(typeof w == 'number'){
26156                 var aw = w - this.wrap.getFrameWidth('lr');
26157                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26158                 ew = aw;
26159             }
26160             if(typeof h == 'number'){
26161                  var tbh = -11;  // fixme it needs to tool bar size!
26162                 for (var i =0; i < this.toolbars.length;i++) {
26163                     // fixme - ask toolbars for heights?
26164                     tbh += this.toolbars[i].el.getHeight();
26165                     //if (this.toolbars[i].footer) {
26166                     //    tbh += this.toolbars[i].footer.el.getHeight();
26167                     //}
26168                 }
26169               
26170                 
26171                 
26172                 
26173                 
26174                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26175                 ah -= 5; // knock a few pixes off for look..
26176                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26177                 var eh = ah;
26178             }
26179         }
26180         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26181         this.editorcore.onResize(ew,eh);
26182         
26183     },
26184
26185     /**
26186      * Toggles the editor between standard and source edit mode.
26187      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26188      */
26189     toggleSourceEdit : function(sourceEditMode)
26190     {
26191         this.editorcore.toggleSourceEdit(sourceEditMode);
26192         
26193         if(this.editorcore.sourceEditMode){
26194             Roo.log('editor - showing textarea');
26195             
26196 //            Roo.log('in');
26197 //            Roo.log(this.syncValue());
26198             this.syncValue();
26199             this.inputEl().removeClass(['hide', 'x-hidden']);
26200             this.inputEl().dom.removeAttribute('tabIndex');
26201             this.inputEl().focus();
26202         }else{
26203             Roo.log('editor - hiding textarea');
26204 //            Roo.log('out')
26205 //            Roo.log(this.pushValue()); 
26206             this.pushValue();
26207             
26208             this.inputEl().addClass(['hide', 'x-hidden']);
26209             this.inputEl().dom.setAttribute('tabIndex', -1);
26210             //this.deferFocus();
26211         }
26212          
26213         if(this.resizable){
26214             this.setSize(this.wrap.getSize());
26215         }
26216         
26217         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26218     },
26219  
26220     // private (for BoxComponent)
26221     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26222
26223     // private (for BoxComponent)
26224     getResizeEl : function(){
26225         return this.wrap;
26226     },
26227
26228     // private (for BoxComponent)
26229     getPositionEl : function(){
26230         return this.wrap;
26231     },
26232
26233     // private
26234     initEvents : function(){
26235         this.originalValue = this.getValue();
26236     },
26237
26238 //    /**
26239 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26240 //     * @method
26241 //     */
26242 //    markInvalid : Roo.emptyFn,
26243 //    /**
26244 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26245 //     * @method
26246 //     */
26247 //    clearInvalid : Roo.emptyFn,
26248
26249     setValue : function(v){
26250         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26251         this.editorcore.pushValue();
26252     },
26253
26254      
26255     // private
26256     deferFocus : function(){
26257         this.focus.defer(10, this);
26258     },
26259
26260     // doc'ed in Field
26261     focus : function(){
26262         this.editorcore.focus();
26263         
26264     },
26265       
26266
26267     // private
26268     onDestroy : function(){
26269         
26270         
26271         
26272         if(this.rendered){
26273             
26274             for (var i =0; i < this.toolbars.length;i++) {
26275                 // fixme - ask toolbars for heights?
26276                 this.toolbars[i].onDestroy();
26277             }
26278             
26279             this.wrap.dom.innerHTML = '';
26280             this.wrap.remove();
26281         }
26282     },
26283
26284     // private
26285     onFirstFocus : function(){
26286         //Roo.log("onFirstFocus");
26287         this.editorcore.onFirstFocus();
26288          for (var i =0; i < this.toolbars.length;i++) {
26289             this.toolbars[i].onFirstFocus();
26290         }
26291         
26292     },
26293     
26294     // private
26295     syncValue : function()
26296     {   
26297         this.editorcore.syncValue();
26298     },
26299     
26300     pushValue : function()
26301     {   
26302         this.editorcore.pushValue();
26303     }
26304      
26305     
26306     // hide stuff that is not compatible
26307     /**
26308      * @event blur
26309      * @hide
26310      */
26311     /**
26312      * @event change
26313      * @hide
26314      */
26315     /**
26316      * @event focus
26317      * @hide
26318      */
26319     /**
26320      * @event specialkey
26321      * @hide
26322      */
26323     /**
26324      * @cfg {String} fieldClass @hide
26325      */
26326     /**
26327      * @cfg {String} focusClass @hide
26328      */
26329     /**
26330      * @cfg {String} autoCreate @hide
26331      */
26332     /**
26333      * @cfg {String} inputType @hide
26334      */
26335      
26336     /**
26337      * @cfg {String} invalidText @hide
26338      */
26339     /**
26340      * @cfg {String} msgFx @hide
26341      */
26342     /**
26343      * @cfg {String} validateOnBlur @hide
26344      */
26345 });
26346  
26347     
26348    
26349    
26350    
26351       
26352 Roo.namespace('Roo.bootstrap.htmleditor');
26353 /**
26354  * @class Roo.bootstrap.HtmlEditorToolbar1
26355  * Basic Toolbar
26356  * 
26357  * @example
26358  * Usage:
26359  *
26360  new Roo.bootstrap.HtmlEditor({
26361     ....
26362     toolbars : [
26363         new Roo.bootstrap.HtmlEditorToolbar1({
26364             disable : { fonts: 1 , format: 1, ..., ... , ...],
26365             btns : [ .... ]
26366         })
26367     }
26368      
26369  * 
26370  * @cfg {Object} disable List of elements to disable..
26371  * @cfg {Array} btns List of additional buttons.
26372  * 
26373  * 
26374  * NEEDS Extra CSS? 
26375  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26376  */
26377  
26378 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26379 {
26380     
26381     Roo.apply(this, config);
26382     
26383     // default disabled, based on 'good practice'..
26384     this.disable = this.disable || {};
26385     Roo.applyIf(this.disable, {
26386         fontSize : true,
26387         colors : true,
26388         specialElements : true
26389     });
26390     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26391     
26392     this.editor = config.editor;
26393     this.editorcore = config.editor.editorcore;
26394     
26395     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26396     
26397     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26398     // dont call parent... till later.
26399 }
26400 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26401      
26402     bar : true,
26403     
26404     editor : false,
26405     editorcore : false,
26406     
26407     
26408     formats : [
26409         "p" ,  
26410         "h1","h2","h3","h4","h5","h6", 
26411         "pre", "code", 
26412         "abbr", "acronym", "address", "cite", "samp", "var",
26413         'div','span'
26414     ],
26415     
26416     onRender : function(ct, position)
26417     {
26418        // Roo.log("Call onRender: " + this.xtype);
26419         
26420        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26421        Roo.log(this.el);
26422        this.el.dom.style.marginBottom = '0';
26423        var _this = this;
26424        var editorcore = this.editorcore;
26425        var editor= this.editor;
26426        
26427        var children = [];
26428        var btn = function(id,cmd , toggle, handler, html){
26429        
26430             var  event = toggle ? 'toggle' : 'click';
26431        
26432             var a = {
26433                 size : 'sm',
26434                 xtype: 'Button',
26435                 xns: Roo.bootstrap,
26436                 //glyphicon : id,
26437                 fa: id,
26438                 cmd : id || cmd,
26439                 enableToggle:toggle !== false,
26440                 html : html || '',
26441                 pressed : toggle ? false : null,
26442                 listeners : {}
26443             };
26444             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26445                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26446             };
26447             children.push(a);
26448             return a;
26449        }
26450        
26451     //    var cb_box = function...
26452         
26453         var style = {
26454                 xtype: 'Button',
26455                 size : 'sm',
26456                 xns: Roo.bootstrap,
26457                 fa : 'font',
26458                 //html : 'submit'
26459                 menu : {
26460                     xtype: 'Menu',
26461                     xns: Roo.bootstrap,
26462                     items:  []
26463                 }
26464         };
26465         Roo.each(this.formats, function(f) {
26466             style.menu.items.push({
26467                 xtype :'MenuItem',
26468                 xns: Roo.bootstrap,
26469                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26470                 tagname : f,
26471                 listeners : {
26472                     click : function()
26473                     {
26474                         editorcore.insertTag(this.tagname);
26475                         editor.focus();
26476                     }
26477                 }
26478                 
26479             });
26480         });
26481         children.push(style);   
26482         
26483         btn('bold',false,true);
26484         btn('italic',false,true);
26485         btn('align-left', 'justifyleft',true);
26486         btn('align-center', 'justifycenter',true);
26487         btn('align-right' , 'justifyright',true);
26488         btn('link', false, false, function(btn) {
26489             //Roo.log("create link?");
26490             var url = prompt(this.createLinkText, this.defaultLinkValue);
26491             if(url && url != 'http:/'+'/'){
26492                 this.editorcore.relayCmd('createlink', url);
26493             }
26494         }),
26495         btn('list','insertunorderedlist',true);
26496         btn('pencil', false,true, function(btn){
26497                 Roo.log(this);
26498                 this.toggleSourceEdit(btn.pressed);
26499         });
26500         
26501         if (this.editor.btns.length > 0) {
26502             for (var i = 0; i<this.editor.btns.length; i++) {
26503                 children.push(this.editor.btns[i]);
26504             }
26505         }
26506         
26507         /*
26508         var cog = {
26509                 xtype: 'Button',
26510                 size : 'sm',
26511                 xns: Roo.bootstrap,
26512                 glyphicon : 'cog',
26513                 //html : 'submit'
26514                 menu : {
26515                     xtype: 'Menu',
26516                     xns: Roo.bootstrap,
26517                     items:  []
26518                 }
26519         };
26520         
26521         cog.menu.items.push({
26522             xtype :'MenuItem',
26523             xns: Roo.bootstrap,
26524             html : Clean styles,
26525             tagname : f,
26526             listeners : {
26527                 click : function()
26528                 {
26529                     editorcore.insertTag(this.tagname);
26530                     editor.focus();
26531                 }
26532             }
26533             
26534         });
26535        */
26536         
26537          
26538        this.xtype = 'NavSimplebar';
26539         
26540         for(var i=0;i< children.length;i++) {
26541             
26542             this.buttons.add(this.addxtypeChild(children[i]));
26543             
26544         }
26545         
26546         editor.on('editorevent', this.updateToolbar, this);
26547     },
26548     onBtnClick : function(id)
26549     {
26550        this.editorcore.relayCmd(id);
26551        this.editorcore.focus();
26552     },
26553     
26554     /**
26555      * Protected method that will not generally be called directly. It triggers
26556      * a toolbar update by reading the markup state of the current selection in the editor.
26557      */
26558     updateToolbar: function(){
26559
26560         if(!this.editorcore.activated){
26561             this.editor.onFirstFocus(); // is this neeed?
26562             return;
26563         }
26564
26565         var btns = this.buttons; 
26566         var doc = this.editorcore.doc;
26567         btns.get('bold').setActive(doc.queryCommandState('bold'));
26568         btns.get('italic').setActive(doc.queryCommandState('italic'));
26569         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26570         
26571         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26572         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26573         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26574         
26575         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26576         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26577          /*
26578         
26579         var ans = this.editorcore.getAllAncestors();
26580         if (this.formatCombo) {
26581             
26582             
26583             var store = this.formatCombo.store;
26584             this.formatCombo.setValue("");
26585             for (var i =0; i < ans.length;i++) {
26586                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26587                     // select it..
26588                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26589                     break;
26590                 }
26591             }
26592         }
26593         
26594         
26595         
26596         // hides menus... - so this cant be on a menu...
26597         Roo.bootstrap.MenuMgr.hideAll();
26598         */
26599         Roo.bootstrap.MenuMgr.hideAll();
26600         //this.editorsyncValue();
26601     },
26602     onFirstFocus: function() {
26603         this.buttons.each(function(item){
26604            item.enable();
26605         });
26606     },
26607     toggleSourceEdit : function(sourceEditMode){
26608         
26609           
26610         if(sourceEditMode){
26611             Roo.log("disabling buttons");
26612            this.buttons.each( function(item){
26613                 if(item.cmd != 'pencil'){
26614                     item.disable();
26615                 }
26616             });
26617           
26618         }else{
26619             Roo.log("enabling buttons");
26620             if(this.editorcore.initialized){
26621                 this.buttons.each( function(item){
26622                     item.enable();
26623                 });
26624             }
26625             
26626         }
26627         Roo.log("calling toggole on editor");
26628         // tell the editor that it's been pressed..
26629         this.editor.toggleSourceEdit(sourceEditMode);
26630        
26631     }
26632 });
26633
26634
26635
26636
26637  
26638 /*
26639  * - LGPL
26640  */
26641
26642 /**
26643  * @class Roo.bootstrap.Markdown
26644  * @extends Roo.bootstrap.TextArea
26645  * Bootstrap Showdown editable area
26646  * @cfg {string} content
26647  * 
26648  * @constructor
26649  * Create a new Showdown
26650  */
26651
26652 Roo.bootstrap.Markdown = function(config){
26653     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26654    
26655 };
26656
26657 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26658     
26659     editing :false,
26660     
26661     initEvents : function()
26662     {
26663         
26664         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26665         this.markdownEl = this.el.createChild({
26666             cls : 'roo-markdown-area'
26667         });
26668         this.inputEl().addClass('d-none');
26669         if (this.getValue() == '') {
26670             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26671             
26672         } else {
26673             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26674         }
26675         this.markdownEl.on('click', this.toggleTextEdit, this);
26676         this.on('blur', this.toggleTextEdit, this);
26677         this.on('specialkey', this.resizeTextArea, this);
26678     },
26679     
26680     toggleTextEdit : function()
26681     {
26682         var sh = this.markdownEl.getHeight();
26683         this.inputEl().addClass('d-none');
26684         this.markdownEl.addClass('d-none');
26685         if (!this.editing) {
26686             // show editor?
26687             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26688             this.inputEl().removeClass('d-none');
26689             this.inputEl().focus();
26690             this.editing = true;
26691             return;
26692         }
26693         // show showdown...
26694         this.updateMarkdown();
26695         this.markdownEl.removeClass('d-none');
26696         this.editing = false;
26697         return;
26698     },
26699     updateMarkdown : function()
26700     {
26701         if (this.getValue() == '') {
26702             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26703             return;
26704         }
26705  
26706         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26707     },
26708     
26709     resizeTextArea: function () {
26710         
26711         var sh = 100;
26712         Roo.log([sh, this.getValue().split("\n").length * 30]);
26713         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26714     },
26715     setValue : function(val)
26716     {
26717         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26718         if (!this.editing) {
26719             this.updateMarkdown();
26720         }
26721         
26722     },
26723     focus : function()
26724     {
26725         if (!this.editing) {
26726             this.toggleTextEdit();
26727         }
26728         
26729     }
26730
26731
26732 });
26733 /**
26734  * @class Roo.bootstrap.Table.AbstractSelectionModel
26735  * @extends Roo.util.Observable
26736  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26737  * implemented by descendant classes.  This class should not be directly instantiated.
26738  * @constructor
26739  */
26740 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26741     this.locked = false;
26742     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26743 };
26744
26745
26746 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26747     /** @ignore Called by the grid automatically. Do not call directly. */
26748     init : function(grid){
26749         this.grid = grid;
26750         this.initEvents();
26751     },
26752
26753     /**
26754      * Locks the selections.
26755      */
26756     lock : function(){
26757         this.locked = true;
26758     },
26759
26760     /**
26761      * Unlocks the selections.
26762      */
26763     unlock : function(){
26764         this.locked = false;
26765     },
26766
26767     /**
26768      * Returns true if the selections are locked.
26769      * @return {Boolean}
26770      */
26771     isLocked : function(){
26772         return this.locked;
26773     },
26774     
26775     
26776     initEvents : function ()
26777     {
26778         
26779     }
26780 });
26781 /**
26782  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26783  * @class Roo.bootstrap.Table.RowSelectionModel
26784  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26785  * It supports multiple selections and keyboard selection/navigation. 
26786  * @constructor
26787  * @param {Object} config
26788  */
26789
26790 Roo.bootstrap.Table.RowSelectionModel = function(config){
26791     Roo.apply(this, config);
26792     this.selections = new Roo.util.MixedCollection(false, function(o){
26793         return o.id;
26794     });
26795
26796     this.last = false;
26797     this.lastActive = false;
26798
26799     this.addEvents({
26800         /**
26801              * @event selectionchange
26802              * Fires when the selection changes
26803              * @param {SelectionModel} this
26804              */
26805             "selectionchange" : true,
26806         /**
26807              * @event afterselectionchange
26808              * Fires after the selection changes (eg. by key press or clicking)
26809              * @param {SelectionModel} this
26810              */
26811             "afterselectionchange" : true,
26812         /**
26813              * @event beforerowselect
26814              * Fires when a row is selected being selected, return false to cancel.
26815              * @param {SelectionModel} this
26816              * @param {Number} rowIndex The selected index
26817              * @param {Boolean} keepExisting False if other selections will be cleared
26818              */
26819             "beforerowselect" : true,
26820         /**
26821              * @event rowselect
26822              * Fires when a row is selected.
26823              * @param {SelectionModel} this
26824              * @param {Number} rowIndex The selected index
26825              * @param {Roo.data.Record} r The record
26826              */
26827             "rowselect" : true,
26828         /**
26829              * @event rowdeselect
26830              * Fires when a row is deselected.
26831              * @param {SelectionModel} this
26832              * @param {Number} rowIndex The selected index
26833              */
26834         "rowdeselect" : true
26835     });
26836     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26837     this.locked = false;
26838  };
26839
26840 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26841     /**
26842      * @cfg {Boolean} singleSelect
26843      * True to allow selection of only one row at a time (defaults to false)
26844      */
26845     singleSelect : false,
26846
26847     // private
26848     initEvents : function()
26849     {
26850
26851         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26852         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26853         //}else{ // allow click to work like normal
26854          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26855         //}
26856         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26857         this.grid.on("rowclick", this.handleMouseDown, this);
26858         
26859         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26860             "up" : function(e){
26861                 if(!e.shiftKey){
26862                     this.selectPrevious(e.shiftKey);
26863                 }else if(this.last !== false && this.lastActive !== false){
26864                     var last = this.last;
26865                     this.selectRange(this.last,  this.lastActive-1);
26866                     this.grid.getView().focusRow(this.lastActive);
26867                     if(last !== false){
26868                         this.last = last;
26869                     }
26870                 }else{
26871                     this.selectFirstRow();
26872                 }
26873                 this.fireEvent("afterselectionchange", this);
26874             },
26875             "down" : function(e){
26876                 if(!e.shiftKey){
26877                     this.selectNext(e.shiftKey);
26878                 }else if(this.last !== false && this.lastActive !== false){
26879                     var last = this.last;
26880                     this.selectRange(this.last,  this.lastActive+1);
26881                     this.grid.getView().focusRow(this.lastActive);
26882                     if(last !== false){
26883                         this.last = last;
26884                     }
26885                 }else{
26886                     this.selectFirstRow();
26887                 }
26888                 this.fireEvent("afterselectionchange", this);
26889             },
26890             scope: this
26891         });
26892         this.grid.store.on('load', function(){
26893             this.selections.clear();
26894         },this);
26895         /*
26896         var view = this.grid.view;
26897         view.on("refresh", this.onRefresh, this);
26898         view.on("rowupdated", this.onRowUpdated, this);
26899         view.on("rowremoved", this.onRemove, this);
26900         */
26901     },
26902
26903     // private
26904     onRefresh : function()
26905     {
26906         var ds = this.grid.store, i, v = this.grid.view;
26907         var s = this.selections;
26908         s.each(function(r){
26909             if((i = ds.indexOfId(r.id)) != -1){
26910                 v.onRowSelect(i);
26911             }else{
26912                 s.remove(r);
26913             }
26914         });
26915     },
26916
26917     // private
26918     onRemove : function(v, index, r){
26919         this.selections.remove(r);
26920     },
26921
26922     // private
26923     onRowUpdated : function(v, index, r){
26924         if(this.isSelected(r)){
26925             v.onRowSelect(index);
26926         }
26927     },
26928
26929     /**
26930      * Select records.
26931      * @param {Array} records The records to select
26932      * @param {Boolean} keepExisting (optional) True to keep existing selections
26933      */
26934     selectRecords : function(records, keepExisting)
26935     {
26936         if(!keepExisting){
26937             this.clearSelections();
26938         }
26939             var ds = this.grid.store;
26940         for(var i = 0, len = records.length; i < len; i++){
26941             this.selectRow(ds.indexOf(records[i]), true);
26942         }
26943     },
26944
26945     /**
26946      * Gets the number of selected rows.
26947      * @return {Number}
26948      */
26949     getCount : function(){
26950         return this.selections.length;
26951     },
26952
26953     /**
26954      * Selects the first row in the grid.
26955      */
26956     selectFirstRow : function(){
26957         this.selectRow(0);
26958     },
26959
26960     /**
26961      * Select the last row.
26962      * @param {Boolean} keepExisting (optional) True to keep existing selections
26963      */
26964     selectLastRow : function(keepExisting){
26965         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26966         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26967     },
26968
26969     /**
26970      * Selects the row immediately following the last selected row.
26971      * @param {Boolean} keepExisting (optional) True to keep existing selections
26972      */
26973     selectNext : function(keepExisting)
26974     {
26975             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26976             this.selectRow(this.last+1, keepExisting);
26977             this.grid.getView().focusRow(this.last);
26978         }
26979     },
26980
26981     /**
26982      * Selects the row that precedes the last selected row.
26983      * @param {Boolean} keepExisting (optional) True to keep existing selections
26984      */
26985     selectPrevious : function(keepExisting){
26986         if(this.last){
26987             this.selectRow(this.last-1, keepExisting);
26988             this.grid.getView().focusRow(this.last);
26989         }
26990     },
26991
26992     /**
26993      * Returns the selected records
26994      * @return {Array} Array of selected records
26995      */
26996     getSelections : function(){
26997         return [].concat(this.selections.items);
26998     },
26999
27000     /**
27001      * Returns the first selected record.
27002      * @return {Record}
27003      */
27004     getSelected : function(){
27005         return this.selections.itemAt(0);
27006     },
27007
27008
27009     /**
27010      * Clears all selections.
27011      */
27012     clearSelections : function(fast)
27013     {
27014         if(this.locked) {
27015             return;
27016         }
27017         if(fast !== true){
27018                 var ds = this.grid.store;
27019             var s = this.selections;
27020             s.each(function(r){
27021                 this.deselectRow(ds.indexOfId(r.id));
27022             }, this);
27023             s.clear();
27024         }else{
27025             this.selections.clear();
27026         }
27027         this.last = false;
27028     },
27029
27030
27031     /**
27032      * Selects all rows.
27033      */
27034     selectAll : function(){
27035         if(this.locked) {
27036             return;
27037         }
27038         this.selections.clear();
27039         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27040             this.selectRow(i, true);
27041         }
27042     },
27043
27044     /**
27045      * Returns True if there is a selection.
27046      * @return {Boolean}
27047      */
27048     hasSelection : function(){
27049         return this.selections.length > 0;
27050     },
27051
27052     /**
27053      * Returns True if the specified row is selected.
27054      * @param {Number/Record} record The record or index of the record to check
27055      * @return {Boolean}
27056      */
27057     isSelected : function(index){
27058             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27059         return (r && this.selections.key(r.id) ? true : false);
27060     },
27061
27062     /**
27063      * Returns True if the specified record id is selected.
27064      * @param {String} id The id of record to check
27065      * @return {Boolean}
27066      */
27067     isIdSelected : function(id){
27068         return (this.selections.key(id) ? true : false);
27069     },
27070
27071
27072     // private
27073     handleMouseDBClick : function(e, t){
27074         
27075     },
27076     // private
27077     handleMouseDown : function(e, t)
27078     {
27079             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27080         if(this.isLocked() || rowIndex < 0 ){
27081             return;
27082         };
27083         if(e.shiftKey && this.last !== false){
27084             var last = this.last;
27085             this.selectRange(last, rowIndex, e.ctrlKey);
27086             this.last = last; // reset the last
27087             t.focus();
27088     
27089         }else{
27090             var isSelected = this.isSelected(rowIndex);
27091             //Roo.log("select row:" + rowIndex);
27092             if(isSelected){
27093                 this.deselectRow(rowIndex);
27094             } else {
27095                         this.selectRow(rowIndex, true);
27096             }
27097     
27098             /*
27099                 if(e.button !== 0 && isSelected){
27100                 alert('rowIndex 2: ' + rowIndex);
27101                     view.focusRow(rowIndex);
27102                 }else if(e.ctrlKey && isSelected){
27103                     this.deselectRow(rowIndex);
27104                 }else if(!isSelected){
27105                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27106                     view.focusRow(rowIndex);
27107                 }
27108             */
27109         }
27110         this.fireEvent("afterselectionchange", this);
27111     },
27112     // private
27113     handleDragableRowClick :  function(grid, rowIndex, e) 
27114     {
27115         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27116             this.selectRow(rowIndex, false);
27117             grid.view.focusRow(rowIndex);
27118              this.fireEvent("afterselectionchange", this);
27119         }
27120     },
27121     
27122     /**
27123      * Selects multiple rows.
27124      * @param {Array} rows Array of the indexes of the row to select
27125      * @param {Boolean} keepExisting (optional) True to keep existing selections
27126      */
27127     selectRows : function(rows, keepExisting){
27128         if(!keepExisting){
27129             this.clearSelections();
27130         }
27131         for(var i = 0, len = rows.length; i < len; i++){
27132             this.selectRow(rows[i], true);
27133         }
27134     },
27135
27136     /**
27137      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27138      * @param {Number} startRow The index of the first row in the range
27139      * @param {Number} endRow The index of the last row in the range
27140      * @param {Boolean} keepExisting (optional) True to retain existing selections
27141      */
27142     selectRange : function(startRow, endRow, keepExisting){
27143         if(this.locked) {
27144             return;
27145         }
27146         if(!keepExisting){
27147             this.clearSelections();
27148         }
27149         if(startRow <= endRow){
27150             for(var i = startRow; i <= endRow; i++){
27151                 this.selectRow(i, true);
27152             }
27153         }else{
27154             for(var i = startRow; i >= endRow; i--){
27155                 this.selectRow(i, true);
27156             }
27157         }
27158     },
27159
27160     /**
27161      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27162      * @param {Number} startRow The index of the first row in the range
27163      * @param {Number} endRow The index of the last row in the range
27164      */
27165     deselectRange : function(startRow, endRow, preventViewNotify){
27166         if(this.locked) {
27167             return;
27168         }
27169         for(var i = startRow; i <= endRow; i++){
27170             this.deselectRow(i, preventViewNotify);
27171         }
27172     },
27173
27174     /**
27175      * Selects a row.
27176      * @param {Number} row The index of the row to select
27177      * @param {Boolean} keepExisting (optional) True to keep existing selections
27178      */
27179     selectRow : function(index, keepExisting, preventViewNotify)
27180     {
27181             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27182             return;
27183         }
27184         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27185             if(!keepExisting || this.singleSelect){
27186                 this.clearSelections();
27187             }
27188             
27189             var r = this.grid.store.getAt(index);
27190             //console.log('selectRow - record id :' + r.id);
27191             
27192             this.selections.add(r);
27193             this.last = this.lastActive = index;
27194             if(!preventViewNotify){
27195                 var proxy = new Roo.Element(
27196                                 this.grid.getRowDom(index)
27197                 );
27198                 proxy.addClass('bg-info info');
27199             }
27200             this.fireEvent("rowselect", this, index, r);
27201             this.fireEvent("selectionchange", this);
27202         }
27203     },
27204
27205     /**
27206      * Deselects a row.
27207      * @param {Number} row The index of the row to deselect
27208      */
27209     deselectRow : function(index, preventViewNotify)
27210     {
27211         if(this.locked) {
27212             return;
27213         }
27214         if(this.last == index){
27215             this.last = false;
27216         }
27217         if(this.lastActive == index){
27218             this.lastActive = false;
27219         }
27220         
27221         var r = this.grid.store.getAt(index);
27222         if (!r) {
27223             return;
27224         }
27225         
27226         this.selections.remove(r);
27227         //.console.log('deselectRow - record id :' + r.id);
27228         if(!preventViewNotify){
27229         
27230             var proxy = new Roo.Element(
27231                 this.grid.getRowDom(index)
27232             );
27233             proxy.removeClass('bg-info info');
27234         }
27235         this.fireEvent("rowdeselect", this, index);
27236         this.fireEvent("selectionchange", this);
27237     },
27238
27239     // private
27240     restoreLast : function(){
27241         if(this._last){
27242             this.last = this._last;
27243         }
27244     },
27245
27246     // private
27247     acceptsNav : function(row, col, cm){
27248         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27249     },
27250
27251     // private
27252     onEditorKey : function(field, e){
27253         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27254         if(k == e.TAB){
27255             e.stopEvent();
27256             ed.completeEdit();
27257             if(e.shiftKey){
27258                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27259             }else{
27260                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27261             }
27262         }else if(k == e.ENTER && !e.ctrlKey){
27263             e.stopEvent();
27264             ed.completeEdit();
27265             if(e.shiftKey){
27266                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27267             }else{
27268                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27269             }
27270         }else if(k == e.ESC){
27271             ed.cancelEdit();
27272         }
27273         if(newCell){
27274             g.startEditing(newCell[0], newCell[1]);
27275         }
27276     }
27277 });
27278 /*
27279  * Based on:
27280  * Ext JS Library 1.1.1
27281  * Copyright(c) 2006-2007, Ext JS, LLC.
27282  *
27283  * Originally Released Under LGPL - original licence link has changed is not relivant.
27284  *
27285  * Fork - LGPL
27286  * <script type="text/javascript">
27287  */
27288  
27289 /**
27290  * @class Roo.bootstrap.PagingToolbar
27291  * @extends Roo.bootstrap.NavSimplebar
27292  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27293  * @constructor
27294  * Create a new PagingToolbar
27295  * @param {Object} config The config object
27296  * @param {Roo.data.Store} store
27297  */
27298 Roo.bootstrap.PagingToolbar = function(config)
27299 {
27300     // old args format still supported... - xtype is prefered..
27301         // created from xtype...
27302     
27303     this.ds = config.dataSource;
27304     
27305     if (config.store && !this.ds) {
27306         this.store= Roo.factory(config.store, Roo.data);
27307         this.ds = this.store;
27308         this.ds.xmodule = this.xmodule || false;
27309     }
27310     
27311     this.toolbarItems = [];
27312     if (config.items) {
27313         this.toolbarItems = config.items;
27314     }
27315     
27316     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27317     
27318     this.cursor = 0;
27319     
27320     if (this.ds) { 
27321         this.bind(this.ds);
27322     }
27323     
27324     if (Roo.bootstrap.version == 4) {
27325         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27326     } else {
27327         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27328     }
27329     
27330 };
27331
27332 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27333     /**
27334      * @cfg {Roo.data.Store} dataSource
27335      * The underlying data store providing the paged data
27336      */
27337     /**
27338      * @cfg {String/HTMLElement/Element} container
27339      * container The id or element that will contain the toolbar
27340      */
27341     /**
27342      * @cfg {Boolean} displayInfo
27343      * True to display the displayMsg (defaults to false)
27344      */
27345     /**
27346      * @cfg {Number} pageSize
27347      * The number of records to display per page (defaults to 20)
27348      */
27349     pageSize: 20,
27350     /**
27351      * @cfg {String} displayMsg
27352      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27353      */
27354     displayMsg : 'Displaying {0} - {1} of {2}',
27355     /**
27356      * @cfg {String} emptyMsg
27357      * The message to display when no records are found (defaults to "No data to display")
27358      */
27359     emptyMsg : 'No data to display',
27360     /**
27361      * Customizable piece of the default paging text (defaults to "Page")
27362      * @type String
27363      */
27364     beforePageText : "Page",
27365     /**
27366      * Customizable piece of the default paging text (defaults to "of %0")
27367      * @type String
27368      */
27369     afterPageText : "of {0}",
27370     /**
27371      * Customizable piece of the default paging text (defaults to "First Page")
27372      * @type String
27373      */
27374     firstText : "First Page",
27375     /**
27376      * Customizable piece of the default paging text (defaults to "Previous Page")
27377      * @type String
27378      */
27379     prevText : "Previous Page",
27380     /**
27381      * Customizable piece of the default paging text (defaults to "Next Page")
27382      * @type String
27383      */
27384     nextText : "Next Page",
27385     /**
27386      * Customizable piece of the default paging text (defaults to "Last Page")
27387      * @type String
27388      */
27389     lastText : "Last Page",
27390     /**
27391      * Customizable piece of the default paging text (defaults to "Refresh")
27392      * @type String
27393      */
27394     refreshText : "Refresh",
27395
27396     buttons : false,
27397     // private
27398     onRender : function(ct, position) 
27399     {
27400         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27401         this.navgroup.parentId = this.id;
27402         this.navgroup.onRender(this.el, null);
27403         // add the buttons to the navgroup
27404         
27405         if(this.displayInfo){
27406             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27407             this.displayEl = this.el.select('.x-paging-info', true).first();
27408 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27409 //            this.displayEl = navel.el.select('span',true).first();
27410         }
27411         
27412         var _this = this;
27413         
27414         if(this.buttons){
27415             Roo.each(_this.buttons, function(e){ // this might need to use render????
27416                Roo.factory(e).render(_this.el);
27417             });
27418         }
27419             
27420         Roo.each(_this.toolbarItems, function(e) {
27421             _this.navgroup.addItem(e);
27422         });
27423         
27424         
27425         this.first = this.navgroup.addItem({
27426             tooltip: this.firstText,
27427             cls: "prev btn-outline-secondary",
27428             html : ' <i class="fa fa-step-backward"></i>',
27429             disabled: true,
27430             preventDefault: true,
27431             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27432         });
27433         
27434         this.prev =  this.navgroup.addItem({
27435             tooltip: this.prevText,
27436             cls: "prev btn-outline-secondary",
27437             html : ' <i class="fa fa-backward"></i>',
27438             disabled: true,
27439             preventDefault: true,
27440             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27441         });
27442     //this.addSeparator();
27443         
27444         
27445         var field = this.navgroup.addItem( {
27446             tagtype : 'span',
27447             cls : 'x-paging-position  btn-outline-secondary',
27448              disabled: true,
27449             html : this.beforePageText  +
27450                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27451                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27452          } ); //?? escaped?
27453         
27454         this.field = field.el.select('input', true).first();
27455         this.field.on("keydown", this.onPagingKeydown, this);
27456         this.field.on("focus", function(){this.dom.select();});
27457     
27458     
27459         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27460         //this.field.setHeight(18);
27461         //this.addSeparator();
27462         this.next = this.navgroup.addItem({
27463             tooltip: this.nextText,
27464             cls: "next btn-outline-secondary",
27465             html : ' <i class="fa fa-forward"></i>',
27466             disabled: true,
27467             preventDefault: true,
27468             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27469         });
27470         this.last = this.navgroup.addItem({
27471             tooltip: this.lastText,
27472             html : ' <i class="fa fa-step-forward"></i>',
27473             cls: "next btn-outline-secondary",
27474             disabled: true,
27475             preventDefault: true,
27476             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27477         });
27478     //this.addSeparator();
27479         this.loading = this.navgroup.addItem({
27480             tooltip: this.refreshText,
27481             cls: "btn-outline-secondary",
27482             html : ' <i class="fa fa-refresh"></i>',
27483             preventDefault: true,
27484             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27485         });
27486         
27487     },
27488
27489     // private
27490     updateInfo : function(){
27491         if(this.displayEl){
27492             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27493             var msg = count == 0 ?
27494                 this.emptyMsg :
27495                 String.format(
27496                     this.displayMsg,
27497                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27498                 );
27499             this.displayEl.update(msg);
27500         }
27501     },
27502
27503     // private
27504     onLoad : function(ds, r, o)
27505     {
27506         this.cursor = o.params && o.params.start ? o.params.start : 0;
27507         
27508         var d = this.getPageData(),
27509             ap = d.activePage,
27510             ps = d.pages;
27511         
27512         
27513         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27514         this.field.dom.value = ap;
27515         this.first.setDisabled(ap == 1);
27516         this.prev.setDisabled(ap == 1);
27517         this.next.setDisabled(ap == ps);
27518         this.last.setDisabled(ap == ps);
27519         this.loading.enable();
27520         this.updateInfo();
27521     },
27522
27523     // private
27524     getPageData : function(){
27525         var total = this.ds.getTotalCount();
27526         return {
27527             total : total,
27528             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27529             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27530         };
27531     },
27532
27533     // private
27534     onLoadError : function(){
27535         this.loading.enable();
27536     },
27537
27538     // private
27539     onPagingKeydown : function(e){
27540         var k = e.getKey();
27541         var d = this.getPageData();
27542         if(k == e.RETURN){
27543             var v = this.field.dom.value, pageNum;
27544             if(!v || isNaN(pageNum = parseInt(v, 10))){
27545                 this.field.dom.value = d.activePage;
27546                 return;
27547             }
27548             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27549             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27550             e.stopEvent();
27551         }
27552         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))
27553         {
27554           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27555           this.field.dom.value = pageNum;
27556           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27557           e.stopEvent();
27558         }
27559         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27560         {
27561           var v = this.field.dom.value, pageNum; 
27562           var increment = (e.shiftKey) ? 10 : 1;
27563           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27564                 increment *= -1;
27565           }
27566           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27567             this.field.dom.value = d.activePage;
27568             return;
27569           }
27570           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27571           {
27572             this.field.dom.value = parseInt(v, 10) + increment;
27573             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27574             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27575           }
27576           e.stopEvent();
27577         }
27578     },
27579
27580     // private
27581     beforeLoad : function(){
27582         if(this.loading){
27583             this.loading.disable();
27584         }
27585     },
27586
27587     // private
27588     onClick : function(which){
27589         
27590         var ds = this.ds;
27591         if (!ds) {
27592             return;
27593         }
27594         
27595         switch(which){
27596             case "first":
27597                 ds.load({params:{start: 0, limit: this.pageSize}});
27598             break;
27599             case "prev":
27600                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27601             break;
27602             case "next":
27603                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27604             break;
27605             case "last":
27606                 var total = ds.getTotalCount();
27607                 var extra = total % this.pageSize;
27608                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27609                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27610             break;
27611             case "refresh":
27612                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27613             break;
27614         }
27615     },
27616
27617     /**
27618      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27619      * @param {Roo.data.Store} store The data store to unbind
27620      */
27621     unbind : function(ds){
27622         ds.un("beforeload", this.beforeLoad, this);
27623         ds.un("load", this.onLoad, this);
27624         ds.un("loadexception", this.onLoadError, this);
27625         ds.un("remove", this.updateInfo, this);
27626         ds.un("add", this.updateInfo, this);
27627         this.ds = undefined;
27628     },
27629
27630     /**
27631      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27632      * @param {Roo.data.Store} store The data store to bind
27633      */
27634     bind : function(ds){
27635         ds.on("beforeload", this.beforeLoad, this);
27636         ds.on("load", this.onLoad, this);
27637         ds.on("loadexception", this.onLoadError, this);
27638         ds.on("remove", this.updateInfo, this);
27639         ds.on("add", this.updateInfo, this);
27640         this.ds = ds;
27641     }
27642 });/*
27643  * - LGPL
27644  *
27645  * element
27646  * 
27647  */
27648
27649 /**
27650  * @class Roo.bootstrap.MessageBar
27651  * @extends Roo.bootstrap.Component
27652  * Bootstrap MessageBar class
27653  * @cfg {String} html contents of the MessageBar
27654  * @cfg {String} weight (info | success | warning | danger) default info
27655  * @cfg {String} beforeClass insert the bar before the given class
27656  * @cfg {Boolean} closable (true | false) default false
27657  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27658  * 
27659  * @constructor
27660  * Create a new Element
27661  * @param {Object} config The config object
27662  */
27663
27664 Roo.bootstrap.MessageBar = function(config){
27665     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27666 };
27667
27668 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27669     
27670     html: '',
27671     weight: 'info',
27672     closable: false,
27673     fixed: false,
27674     beforeClass: 'bootstrap-sticky-wrap',
27675     
27676     getAutoCreate : function(){
27677         
27678         var cfg = {
27679             tag: 'div',
27680             cls: 'alert alert-dismissable alert-' + this.weight,
27681             cn: [
27682                 {
27683                     tag: 'span',
27684                     cls: 'message',
27685                     html: this.html || ''
27686                 }
27687             ]
27688         };
27689         
27690         if(this.fixed){
27691             cfg.cls += ' alert-messages-fixed';
27692         }
27693         
27694         if(this.closable){
27695             cfg.cn.push({
27696                 tag: 'button',
27697                 cls: 'close',
27698                 html: 'x'
27699             });
27700         }
27701         
27702         return cfg;
27703     },
27704     
27705     onRender : function(ct, position)
27706     {
27707         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27708         
27709         if(!this.el){
27710             var cfg = Roo.apply({},  this.getAutoCreate());
27711             cfg.id = Roo.id();
27712             
27713             if (this.cls) {
27714                 cfg.cls += ' ' + this.cls;
27715             }
27716             if (this.style) {
27717                 cfg.style = this.style;
27718             }
27719             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27720             
27721             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27722         }
27723         
27724         this.el.select('>button.close').on('click', this.hide, this);
27725         
27726     },
27727     
27728     show : function()
27729     {
27730         if (!this.rendered) {
27731             this.render();
27732         }
27733         
27734         this.el.show();
27735         
27736         this.fireEvent('show', this);
27737         
27738     },
27739     
27740     hide : function()
27741     {
27742         if (!this.rendered) {
27743             this.render();
27744         }
27745         
27746         this.el.hide();
27747         
27748         this.fireEvent('hide', this);
27749     },
27750     
27751     update : function()
27752     {
27753 //        var e = this.el.dom.firstChild;
27754 //        
27755 //        if(this.closable){
27756 //            e = e.nextSibling;
27757 //        }
27758 //        
27759 //        e.data = this.html || '';
27760
27761         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27762     }
27763    
27764 });
27765
27766  
27767
27768      /*
27769  * - LGPL
27770  *
27771  * Graph
27772  * 
27773  */
27774
27775
27776 /**
27777  * @class Roo.bootstrap.Graph
27778  * @extends Roo.bootstrap.Component
27779  * Bootstrap Graph class
27780 > Prameters
27781  -sm {number} sm 4
27782  -md {number} md 5
27783  @cfg {String} graphtype  bar | vbar | pie
27784  @cfg {number} g_x coodinator | centre x (pie)
27785  @cfg {number} g_y coodinator | centre y (pie)
27786  @cfg {number} g_r radius (pie)
27787  @cfg {number} g_height height of the chart (respected by all elements in the set)
27788  @cfg {number} g_width width of the chart (respected by all elements in the set)
27789  @cfg {Object} title The title of the chart
27790     
27791  -{Array}  values
27792  -opts (object) options for the chart 
27793      o {
27794      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27795      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27796      o vgutter (number)
27797      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.
27798      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27799      o to
27800      o stretch (boolean)
27801      o }
27802  -opts (object) options for the pie
27803      o{
27804      o cut
27805      o startAngle (number)
27806      o endAngle (number)
27807      } 
27808  *
27809  * @constructor
27810  * Create a new Input
27811  * @param {Object} config The config object
27812  */
27813
27814 Roo.bootstrap.Graph = function(config){
27815     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27816     
27817     this.addEvents({
27818         // img events
27819         /**
27820          * @event click
27821          * The img click event for the img.
27822          * @param {Roo.EventObject} e
27823          */
27824         "click" : true
27825     });
27826 };
27827
27828 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27829     
27830     sm: 4,
27831     md: 5,
27832     graphtype: 'bar',
27833     g_height: 250,
27834     g_width: 400,
27835     g_x: 50,
27836     g_y: 50,
27837     g_r: 30,
27838     opts:{
27839         //g_colors: this.colors,
27840         g_type: 'soft',
27841         g_gutter: '20%'
27842
27843     },
27844     title : false,
27845
27846     getAutoCreate : function(){
27847         
27848         var cfg = {
27849             tag: 'div',
27850             html : null
27851         };
27852         
27853         
27854         return  cfg;
27855     },
27856
27857     onRender : function(ct,position){
27858         
27859         
27860         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27861         
27862         if (typeof(Raphael) == 'undefined') {
27863             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27864             return;
27865         }
27866         
27867         this.raphael = Raphael(this.el.dom);
27868         
27869                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27870                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27871                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27872                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27873                 /*
27874                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27875                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27876                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27877                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27878                 
27879                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27880                 r.barchart(330, 10, 300, 220, data1);
27881                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27882                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27883                 */
27884                 
27885                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27886                 // r.barchart(30, 30, 560, 250,  xdata, {
27887                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27888                 //     axis : "0 0 1 1",
27889                 //     axisxlabels :  xdata
27890                 //     //yvalues : cols,
27891                    
27892                 // });
27893 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27894 //        
27895 //        this.load(null,xdata,{
27896 //                axis : "0 0 1 1",
27897 //                axisxlabels :  xdata
27898 //                });
27899
27900     },
27901
27902     load : function(graphtype,xdata,opts)
27903     {
27904         this.raphael.clear();
27905         if(!graphtype) {
27906             graphtype = this.graphtype;
27907         }
27908         if(!opts){
27909             opts = this.opts;
27910         }
27911         var r = this.raphael,
27912             fin = function () {
27913                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27914             },
27915             fout = function () {
27916                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27917             },
27918             pfin = function() {
27919                 this.sector.stop();
27920                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27921
27922                 if (this.label) {
27923                     this.label[0].stop();
27924                     this.label[0].attr({ r: 7.5 });
27925                     this.label[1].attr({ "font-weight": 800 });
27926                 }
27927             },
27928             pfout = function() {
27929                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27930
27931                 if (this.label) {
27932                     this.label[0].animate({ r: 5 }, 500, "bounce");
27933                     this.label[1].attr({ "font-weight": 400 });
27934                 }
27935             };
27936
27937         switch(graphtype){
27938             case 'bar':
27939                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27940                 break;
27941             case 'hbar':
27942                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27943                 break;
27944             case 'pie':
27945 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27946 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27947 //            
27948                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27949                 
27950                 break;
27951
27952         }
27953         
27954         if(this.title){
27955             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27956         }
27957         
27958     },
27959     
27960     setTitle: function(o)
27961     {
27962         this.title = o;
27963     },
27964     
27965     initEvents: function() {
27966         
27967         if(!this.href){
27968             this.el.on('click', this.onClick, this);
27969         }
27970     },
27971     
27972     onClick : function(e)
27973     {
27974         Roo.log('img onclick');
27975         this.fireEvent('click', this, e);
27976     }
27977    
27978 });
27979
27980  
27981 /*
27982  * - LGPL
27983  *
27984  * numberBox
27985  * 
27986  */
27987 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27988
27989 /**
27990  * @class Roo.bootstrap.dash.NumberBox
27991  * @extends Roo.bootstrap.Component
27992  * Bootstrap NumberBox class
27993  * @cfg {String} headline Box headline
27994  * @cfg {String} content Box content
27995  * @cfg {String} icon Box icon
27996  * @cfg {String} footer Footer text
27997  * @cfg {String} fhref Footer href
27998  * 
27999  * @constructor
28000  * Create a new NumberBox
28001  * @param {Object} config The config object
28002  */
28003
28004
28005 Roo.bootstrap.dash.NumberBox = function(config){
28006     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28007     
28008 };
28009
28010 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28011     
28012     headline : '',
28013     content : '',
28014     icon : '',
28015     footer : '',
28016     fhref : '',
28017     ficon : '',
28018     
28019     getAutoCreate : function(){
28020         
28021         var cfg = {
28022             tag : 'div',
28023             cls : 'small-box ',
28024             cn : [
28025                 {
28026                     tag : 'div',
28027                     cls : 'inner',
28028                     cn :[
28029                         {
28030                             tag : 'h3',
28031                             cls : 'roo-headline',
28032                             html : this.headline
28033                         },
28034                         {
28035                             tag : 'p',
28036                             cls : 'roo-content',
28037                             html : this.content
28038                         }
28039                     ]
28040                 }
28041             ]
28042         };
28043         
28044         if(this.icon){
28045             cfg.cn.push({
28046                 tag : 'div',
28047                 cls : 'icon',
28048                 cn :[
28049                     {
28050                         tag : 'i',
28051                         cls : 'ion ' + this.icon
28052                     }
28053                 ]
28054             });
28055         }
28056         
28057         if(this.footer){
28058             var footer = {
28059                 tag : 'a',
28060                 cls : 'small-box-footer',
28061                 href : this.fhref || '#',
28062                 html : this.footer
28063             };
28064             
28065             cfg.cn.push(footer);
28066             
28067         }
28068         
28069         return  cfg;
28070     },
28071
28072     onRender : function(ct,position){
28073         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28074
28075
28076        
28077                 
28078     },
28079
28080     setHeadline: function (value)
28081     {
28082         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28083     },
28084     
28085     setFooter: function (value, href)
28086     {
28087         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28088         
28089         if(href){
28090             this.el.select('a.small-box-footer',true).first().attr('href', href);
28091         }
28092         
28093     },
28094
28095     setContent: function (value)
28096     {
28097         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28098     },
28099
28100     initEvents: function() 
28101     {   
28102         
28103     }
28104     
28105 });
28106
28107  
28108 /*
28109  * - LGPL
28110  *
28111  * TabBox
28112  * 
28113  */
28114 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28115
28116 /**
28117  * @class Roo.bootstrap.dash.TabBox
28118  * @extends Roo.bootstrap.Component
28119  * Bootstrap TabBox class
28120  * @cfg {String} title Title of the TabBox
28121  * @cfg {String} icon Icon of the TabBox
28122  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28123  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28124  * 
28125  * @constructor
28126  * Create a new TabBox
28127  * @param {Object} config The config object
28128  */
28129
28130
28131 Roo.bootstrap.dash.TabBox = function(config){
28132     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28133     this.addEvents({
28134         // raw events
28135         /**
28136          * @event addpane
28137          * When a pane is added
28138          * @param {Roo.bootstrap.dash.TabPane} pane
28139          */
28140         "addpane" : true,
28141         /**
28142          * @event activatepane
28143          * When a pane is activated
28144          * @param {Roo.bootstrap.dash.TabPane} pane
28145          */
28146         "activatepane" : true
28147         
28148          
28149     });
28150     
28151     this.panes = [];
28152 };
28153
28154 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28155
28156     title : '',
28157     icon : false,
28158     showtabs : true,
28159     tabScrollable : false,
28160     
28161     getChildContainer : function()
28162     {
28163         return this.el.select('.tab-content', true).first();
28164     },
28165     
28166     getAutoCreate : function(){
28167         
28168         var header = {
28169             tag: 'li',
28170             cls: 'pull-left header',
28171             html: this.title,
28172             cn : []
28173         };
28174         
28175         if(this.icon){
28176             header.cn.push({
28177                 tag: 'i',
28178                 cls: 'fa ' + this.icon
28179             });
28180         }
28181         
28182         var h = {
28183             tag: 'ul',
28184             cls: 'nav nav-tabs pull-right',
28185             cn: [
28186                 header
28187             ]
28188         };
28189         
28190         if(this.tabScrollable){
28191             h = {
28192                 tag: 'div',
28193                 cls: 'tab-header',
28194                 cn: [
28195                     {
28196                         tag: 'ul',
28197                         cls: 'nav nav-tabs pull-right',
28198                         cn: [
28199                             header
28200                         ]
28201                     }
28202                 ]
28203             };
28204         }
28205         
28206         var cfg = {
28207             tag: 'div',
28208             cls: 'nav-tabs-custom',
28209             cn: [
28210                 h,
28211                 {
28212                     tag: 'div',
28213                     cls: 'tab-content no-padding',
28214                     cn: []
28215                 }
28216             ]
28217         };
28218
28219         return  cfg;
28220     },
28221     initEvents : function()
28222     {
28223         //Roo.log('add add pane handler');
28224         this.on('addpane', this.onAddPane, this);
28225     },
28226      /**
28227      * Updates the box title
28228      * @param {String} html to set the title to.
28229      */
28230     setTitle : function(value)
28231     {
28232         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28233     },
28234     onAddPane : function(pane)
28235     {
28236         this.panes.push(pane);
28237         //Roo.log('addpane');
28238         //Roo.log(pane);
28239         // tabs are rendere left to right..
28240         if(!this.showtabs){
28241             return;
28242         }
28243         
28244         var ctr = this.el.select('.nav-tabs', true).first();
28245          
28246          
28247         var existing = ctr.select('.nav-tab',true);
28248         var qty = existing.getCount();;
28249         
28250         
28251         var tab = ctr.createChild({
28252             tag : 'li',
28253             cls : 'nav-tab' + (qty ? '' : ' active'),
28254             cn : [
28255                 {
28256                     tag : 'a',
28257                     href:'#',
28258                     html : pane.title
28259                 }
28260             ]
28261         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28262         pane.tab = tab;
28263         
28264         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28265         if (!qty) {
28266             pane.el.addClass('active');
28267         }
28268         
28269                 
28270     },
28271     onTabClick : function(ev,un,ob,pane)
28272     {
28273         //Roo.log('tab - prev default');
28274         ev.preventDefault();
28275         
28276         
28277         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28278         pane.tab.addClass('active');
28279         //Roo.log(pane.title);
28280         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28281         // technically we should have a deactivate event.. but maybe add later.
28282         // and it should not de-activate the selected tab...
28283         this.fireEvent('activatepane', pane);
28284         pane.el.addClass('active');
28285         pane.fireEvent('activate');
28286         
28287         
28288     },
28289     
28290     getActivePane : function()
28291     {
28292         var r = false;
28293         Roo.each(this.panes, function(p) {
28294             if(p.el.hasClass('active')){
28295                 r = p;
28296                 return false;
28297             }
28298             
28299             return;
28300         });
28301         
28302         return r;
28303     }
28304     
28305     
28306 });
28307
28308  
28309 /*
28310  * - LGPL
28311  *
28312  * Tab pane
28313  * 
28314  */
28315 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28316 /**
28317  * @class Roo.bootstrap.TabPane
28318  * @extends Roo.bootstrap.Component
28319  * Bootstrap TabPane class
28320  * @cfg {Boolean} active (false | true) Default false
28321  * @cfg {String} title title of panel
28322
28323  * 
28324  * @constructor
28325  * Create a new TabPane
28326  * @param {Object} config The config object
28327  */
28328
28329 Roo.bootstrap.dash.TabPane = function(config){
28330     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28331     
28332     this.addEvents({
28333         // raw events
28334         /**
28335          * @event activate
28336          * When a pane is activated
28337          * @param {Roo.bootstrap.dash.TabPane} pane
28338          */
28339         "activate" : true
28340          
28341     });
28342 };
28343
28344 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28345     
28346     active : false,
28347     title : '',
28348     
28349     // the tabBox that this is attached to.
28350     tab : false,
28351      
28352     getAutoCreate : function() 
28353     {
28354         var cfg = {
28355             tag: 'div',
28356             cls: 'tab-pane'
28357         };
28358         
28359         if(this.active){
28360             cfg.cls += ' active';
28361         }
28362         
28363         return cfg;
28364     },
28365     initEvents  : function()
28366     {
28367         //Roo.log('trigger add pane handler');
28368         this.parent().fireEvent('addpane', this)
28369     },
28370     
28371      /**
28372      * Updates the tab title 
28373      * @param {String} html to set the title to.
28374      */
28375     setTitle: function(str)
28376     {
28377         if (!this.tab) {
28378             return;
28379         }
28380         this.title = str;
28381         this.tab.select('a', true).first().dom.innerHTML = str;
28382         
28383     }
28384     
28385     
28386     
28387 });
28388
28389  
28390
28391
28392  /*
28393  * - LGPL
28394  *
28395  * menu
28396  * 
28397  */
28398 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28399
28400 /**
28401  * @class Roo.bootstrap.menu.Menu
28402  * @extends Roo.bootstrap.Component
28403  * Bootstrap Menu class - container for Menu
28404  * @cfg {String} html Text of the menu
28405  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28406  * @cfg {String} icon Font awesome icon
28407  * @cfg {String} pos Menu align to (top | bottom) default bottom
28408  * 
28409  * 
28410  * @constructor
28411  * Create a new Menu
28412  * @param {Object} config The config object
28413  */
28414
28415
28416 Roo.bootstrap.menu.Menu = function(config){
28417     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28418     
28419     this.addEvents({
28420         /**
28421          * @event beforeshow
28422          * Fires before this menu is displayed
28423          * @param {Roo.bootstrap.menu.Menu} this
28424          */
28425         beforeshow : true,
28426         /**
28427          * @event beforehide
28428          * Fires before this menu is hidden
28429          * @param {Roo.bootstrap.menu.Menu} this
28430          */
28431         beforehide : true,
28432         /**
28433          * @event show
28434          * Fires after this menu is displayed
28435          * @param {Roo.bootstrap.menu.Menu} this
28436          */
28437         show : true,
28438         /**
28439          * @event hide
28440          * Fires after this menu is hidden
28441          * @param {Roo.bootstrap.menu.Menu} this
28442          */
28443         hide : true,
28444         /**
28445          * @event click
28446          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28447          * @param {Roo.bootstrap.menu.Menu} this
28448          * @param {Roo.EventObject} e
28449          */
28450         click : true
28451     });
28452     
28453 };
28454
28455 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28456     
28457     submenu : false,
28458     html : '',
28459     weight : 'default',
28460     icon : false,
28461     pos : 'bottom',
28462     
28463     
28464     getChildContainer : function() {
28465         if(this.isSubMenu){
28466             return this.el;
28467         }
28468         
28469         return this.el.select('ul.dropdown-menu', true).first();  
28470     },
28471     
28472     getAutoCreate : function()
28473     {
28474         var text = [
28475             {
28476                 tag : 'span',
28477                 cls : 'roo-menu-text',
28478                 html : this.html
28479             }
28480         ];
28481         
28482         if(this.icon){
28483             text.unshift({
28484                 tag : 'i',
28485                 cls : 'fa ' + this.icon
28486             })
28487         }
28488         
28489         
28490         var cfg = {
28491             tag : 'div',
28492             cls : 'btn-group',
28493             cn : [
28494                 {
28495                     tag : 'button',
28496                     cls : 'dropdown-button btn btn-' + this.weight,
28497                     cn : text
28498                 },
28499                 {
28500                     tag : 'button',
28501                     cls : 'dropdown-toggle btn btn-' + this.weight,
28502                     cn : [
28503                         {
28504                             tag : 'span',
28505                             cls : 'caret'
28506                         }
28507                     ]
28508                 },
28509                 {
28510                     tag : 'ul',
28511                     cls : 'dropdown-menu'
28512                 }
28513             ]
28514             
28515         };
28516         
28517         if(this.pos == 'top'){
28518             cfg.cls += ' dropup';
28519         }
28520         
28521         if(this.isSubMenu){
28522             cfg = {
28523                 tag : 'ul',
28524                 cls : 'dropdown-menu'
28525             }
28526         }
28527         
28528         return cfg;
28529     },
28530     
28531     onRender : function(ct, position)
28532     {
28533         this.isSubMenu = ct.hasClass('dropdown-submenu');
28534         
28535         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28536     },
28537     
28538     initEvents : function() 
28539     {
28540         if(this.isSubMenu){
28541             return;
28542         }
28543         
28544         this.hidden = true;
28545         
28546         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28547         this.triggerEl.on('click', this.onTriggerPress, this);
28548         
28549         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28550         this.buttonEl.on('click', this.onClick, this);
28551         
28552     },
28553     
28554     list : function()
28555     {
28556         if(this.isSubMenu){
28557             return this.el;
28558         }
28559         
28560         return this.el.select('ul.dropdown-menu', true).first();
28561     },
28562     
28563     onClick : function(e)
28564     {
28565         this.fireEvent("click", this, e);
28566     },
28567     
28568     onTriggerPress  : function(e)
28569     {   
28570         if (this.isVisible()) {
28571             this.hide();
28572         } else {
28573             this.show();
28574         }
28575     },
28576     
28577     isVisible : function(){
28578         return !this.hidden;
28579     },
28580     
28581     show : function()
28582     {
28583         this.fireEvent("beforeshow", this);
28584         
28585         this.hidden = false;
28586         this.el.addClass('open');
28587         
28588         Roo.get(document).on("mouseup", this.onMouseUp, this);
28589         
28590         this.fireEvent("show", this);
28591         
28592         
28593     },
28594     
28595     hide : function()
28596     {
28597         this.fireEvent("beforehide", this);
28598         
28599         this.hidden = true;
28600         this.el.removeClass('open');
28601         
28602         Roo.get(document).un("mouseup", this.onMouseUp);
28603         
28604         this.fireEvent("hide", this);
28605     },
28606     
28607     onMouseUp : function()
28608     {
28609         this.hide();
28610     }
28611     
28612 });
28613
28614  
28615  /*
28616  * - LGPL
28617  *
28618  * menu item
28619  * 
28620  */
28621 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28622
28623 /**
28624  * @class Roo.bootstrap.menu.Item
28625  * @extends Roo.bootstrap.Component
28626  * Bootstrap MenuItem class
28627  * @cfg {Boolean} submenu (true | false) default false
28628  * @cfg {String} html text of the item
28629  * @cfg {String} href the link
28630  * @cfg {Boolean} disable (true | false) default false
28631  * @cfg {Boolean} preventDefault (true | false) default true
28632  * @cfg {String} icon Font awesome icon
28633  * @cfg {String} pos Submenu align to (left | right) default right 
28634  * 
28635  * 
28636  * @constructor
28637  * Create a new Item
28638  * @param {Object} config The config object
28639  */
28640
28641
28642 Roo.bootstrap.menu.Item = function(config){
28643     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28644     this.addEvents({
28645         /**
28646          * @event mouseover
28647          * Fires when the mouse is hovering over this menu
28648          * @param {Roo.bootstrap.menu.Item} this
28649          * @param {Roo.EventObject} e
28650          */
28651         mouseover : true,
28652         /**
28653          * @event mouseout
28654          * Fires when the mouse exits this menu
28655          * @param {Roo.bootstrap.menu.Item} this
28656          * @param {Roo.EventObject} e
28657          */
28658         mouseout : true,
28659         // raw events
28660         /**
28661          * @event click
28662          * The raw click event for the entire grid.
28663          * @param {Roo.EventObject} e
28664          */
28665         click : true
28666     });
28667 };
28668
28669 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28670     
28671     submenu : false,
28672     href : '',
28673     html : '',
28674     preventDefault: true,
28675     disable : false,
28676     icon : false,
28677     pos : 'right',
28678     
28679     getAutoCreate : function()
28680     {
28681         var text = [
28682             {
28683                 tag : 'span',
28684                 cls : 'roo-menu-item-text',
28685                 html : this.html
28686             }
28687         ];
28688         
28689         if(this.icon){
28690             text.unshift({
28691                 tag : 'i',
28692                 cls : 'fa ' + this.icon
28693             })
28694         }
28695         
28696         var cfg = {
28697             tag : 'li',
28698             cn : [
28699                 {
28700                     tag : 'a',
28701                     href : this.href || '#',
28702                     cn : text
28703                 }
28704             ]
28705         };
28706         
28707         if(this.disable){
28708             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28709         }
28710         
28711         if(this.submenu){
28712             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28713             
28714             if(this.pos == 'left'){
28715                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28716             }
28717         }
28718         
28719         return cfg;
28720     },
28721     
28722     initEvents : function() 
28723     {
28724         this.el.on('mouseover', this.onMouseOver, this);
28725         this.el.on('mouseout', this.onMouseOut, this);
28726         
28727         this.el.select('a', true).first().on('click', this.onClick, this);
28728         
28729     },
28730     
28731     onClick : function(e)
28732     {
28733         if(this.preventDefault){
28734             e.preventDefault();
28735         }
28736         
28737         this.fireEvent("click", this, e);
28738     },
28739     
28740     onMouseOver : function(e)
28741     {
28742         if(this.submenu && this.pos == 'left'){
28743             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28744         }
28745         
28746         this.fireEvent("mouseover", this, e);
28747     },
28748     
28749     onMouseOut : function(e)
28750     {
28751         this.fireEvent("mouseout", this, e);
28752     }
28753 });
28754
28755  
28756
28757  /*
28758  * - LGPL
28759  *
28760  * menu separator
28761  * 
28762  */
28763 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28764
28765 /**
28766  * @class Roo.bootstrap.menu.Separator
28767  * @extends Roo.bootstrap.Component
28768  * Bootstrap Separator class
28769  * 
28770  * @constructor
28771  * Create a new Separator
28772  * @param {Object} config The config object
28773  */
28774
28775
28776 Roo.bootstrap.menu.Separator = function(config){
28777     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28778 };
28779
28780 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28781     
28782     getAutoCreate : function(){
28783         var cfg = {
28784             tag : 'li',
28785             cls: 'divider'
28786         };
28787         
28788         return cfg;
28789     }
28790    
28791 });
28792
28793  
28794
28795  /*
28796  * - LGPL
28797  *
28798  * Tooltip
28799  * 
28800  */
28801
28802 /**
28803  * @class Roo.bootstrap.Tooltip
28804  * Bootstrap Tooltip class
28805  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28806  * to determine which dom element triggers the tooltip.
28807  * 
28808  * It needs to add support for additional attributes like tooltip-position
28809  * 
28810  * @constructor
28811  * Create a new Toolti
28812  * @param {Object} config The config object
28813  */
28814
28815 Roo.bootstrap.Tooltip = function(config){
28816     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28817     
28818     this.alignment = Roo.bootstrap.Tooltip.alignment;
28819     
28820     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28821         this.alignment = config.alignment;
28822     }
28823     
28824 };
28825
28826 Roo.apply(Roo.bootstrap.Tooltip, {
28827     /**
28828      * @function init initialize tooltip monitoring.
28829      * @static
28830      */
28831     currentEl : false,
28832     currentTip : false,
28833     currentRegion : false,
28834     
28835     //  init : delay?
28836     
28837     init : function()
28838     {
28839         Roo.get(document).on('mouseover', this.enter ,this);
28840         Roo.get(document).on('mouseout', this.leave, this);
28841          
28842         
28843         this.currentTip = new Roo.bootstrap.Tooltip();
28844     },
28845     
28846     enter : function(ev)
28847     {
28848         var dom = ev.getTarget();
28849         
28850         //Roo.log(['enter',dom]);
28851         var el = Roo.fly(dom);
28852         if (this.currentEl) {
28853             //Roo.log(dom);
28854             //Roo.log(this.currentEl);
28855             //Roo.log(this.currentEl.contains(dom));
28856             if (this.currentEl == el) {
28857                 return;
28858             }
28859             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28860                 return;
28861             }
28862
28863         }
28864         
28865         if (this.currentTip.el) {
28866             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28867         }    
28868         //Roo.log(ev);
28869         
28870         if(!el || el.dom == document){
28871             return;
28872         }
28873         
28874         var bindEl = el;
28875         
28876         // you can not look for children, as if el is the body.. then everythign is the child..
28877         if (!el.attr('tooltip')) { //
28878             if (!el.select("[tooltip]").elements.length) {
28879                 return;
28880             }
28881             // is the mouse over this child...?
28882             bindEl = el.select("[tooltip]").first();
28883             var xy = ev.getXY();
28884             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28885                 //Roo.log("not in region.");
28886                 return;
28887             }
28888             //Roo.log("child element over..");
28889             
28890         }
28891         this.currentEl = bindEl;
28892         this.currentTip.bind(bindEl);
28893         this.currentRegion = Roo.lib.Region.getRegion(dom);
28894         this.currentTip.enter();
28895         
28896     },
28897     leave : function(ev)
28898     {
28899         var dom = ev.getTarget();
28900         //Roo.log(['leave',dom]);
28901         if (!this.currentEl) {
28902             return;
28903         }
28904         
28905         
28906         if (dom != this.currentEl.dom) {
28907             return;
28908         }
28909         var xy = ev.getXY();
28910         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28911             return;
28912         }
28913         // only activate leave if mouse cursor is outside... bounding box..
28914         
28915         
28916         
28917         
28918         if (this.currentTip) {
28919             this.currentTip.leave();
28920         }
28921         //Roo.log('clear currentEl');
28922         this.currentEl = false;
28923         
28924         
28925     },
28926     alignment : {
28927         'left' : ['r-l', [-2,0], 'right'],
28928         'right' : ['l-r', [2,0], 'left'],
28929         'bottom' : ['t-b', [0,2], 'top'],
28930         'top' : [ 'b-t', [0,-2], 'bottom']
28931     }
28932     
28933 });
28934
28935
28936 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28937     
28938     
28939     bindEl : false,
28940     
28941     delay : null, // can be { show : 300 , hide: 500}
28942     
28943     timeout : null,
28944     
28945     hoverState : null, //???
28946     
28947     placement : 'bottom', 
28948     
28949     alignment : false,
28950     
28951     getAutoCreate : function(){
28952     
28953         var cfg = {
28954            cls : 'tooltip',   
28955            role : 'tooltip',
28956            cn : [
28957                 {
28958                     cls : 'tooltip-arrow arrow'
28959                 },
28960                 {
28961                     cls : 'tooltip-inner'
28962                 }
28963            ]
28964         };
28965         
28966         return cfg;
28967     },
28968     bind : function(el)
28969     {
28970         this.bindEl = el;
28971     },
28972     
28973     initEvents : function()
28974     {
28975         this.arrowEl = this.el.select('.arrow', true).first();
28976         this.innerEl = this.el.select('.tooltip-inner', true).first();
28977     },
28978     
28979     enter : function () {
28980        
28981         if (this.timeout != null) {
28982             clearTimeout(this.timeout);
28983         }
28984         
28985         this.hoverState = 'in';
28986          //Roo.log("enter - show");
28987         if (!this.delay || !this.delay.show) {
28988             this.show();
28989             return;
28990         }
28991         var _t = this;
28992         this.timeout = setTimeout(function () {
28993             if (_t.hoverState == 'in') {
28994                 _t.show();
28995             }
28996         }, this.delay.show);
28997     },
28998     leave : function()
28999     {
29000         clearTimeout(this.timeout);
29001     
29002         this.hoverState = 'out';
29003          if (!this.delay || !this.delay.hide) {
29004             this.hide();
29005             return;
29006         }
29007        
29008         var _t = this;
29009         this.timeout = setTimeout(function () {
29010             //Roo.log("leave - timeout");
29011             
29012             if (_t.hoverState == 'out') {
29013                 _t.hide();
29014                 Roo.bootstrap.Tooltip.currentEl = false;
29015             }
29016         }, delay);
29017     },
29018     
29019     show : function (msg)
29020     {
29021         if (!this.el) {
29022             this.render(document.body);
29023         }
29024         // set content.
29025         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29026         
29027         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29028         
29029         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29030         
29031         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29032                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29033         
29034         var placement = typeof this.placement == 'function' ?
29035             this.placement.call(this, this.el, on_el) :
29036             this.placement;
29037             
29038         var autoToken = /\s?auto?\s?/i;
29039         var autoPlace = autoToken.test(placement);
29040         if (autoPlace) {
29041             placement = placement.replace(autoToken, '') || 'top';
29042         }
29043         
29044         //this.el.detach()
29045         //this.el.setXY([0,0]);
29046         this.el.show();
29047         //this.el.dom.style.display='block';
29048         
29049         //this.el.appendTo(on_el);
29050         
29051         var p = this.getPosition();
29052         var box = this.el.getBox();
29053         
29054         if (autoPlace) {
29055             // fixme..
29056         }
29057         
29058         var align = this.alignment[placement];
29059         
29060         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29061         
29062         if(placement == 'top' || placement == 'bottom'){
29063             if(xy[0] < 0){
29064                 placement = 'right';
29065             }
29066             
29067             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29068                 placement = 'left';
29069             }
29070             
29071             var scroll = Roo.select('body', true).first().getScroll();
29072             
29073             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29074                 placement = 'top';
29075             }
29076             
29077             align = this.alignment[placement];
29078             
29079             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29080             
29081         }
29082         
29083         this.el.alignTo(this.bindEl, align[0],align[1]);
29084         //var arrow = this.el.select('.arrow',true).first();
29085         //arrow.set(align[2], 
29086         
29087         this.el.addClass(placement);
29088         this.el.addClass("bs-tooltip-"+ placement);
29089         
29090         this.el.addClass('in fade show');
29091         
29092         this.hoverState = null;
29093         
29094         if (this.el.hasClass('fade')) {
29095             // fade it?
29096         }
29097         
29098         
29099         
29100         
29101         
29102     },
29103     hide : function()
29104     {
29105          
29106         if (!this.el) {
29107             return;
29108         }
29109         //this.el.setXY([0,0]);
29110         this.el.removeClass(['show', 'in']);
29111         //this.el.hide();
29112         
29113     }
29114     
29115 });
29116  
29117
29118  /*
29119  * - LGPL
29120  *
29121  * Location Picker
29122  * 
29123  */
29124
29125 /**
29126  * @class Roo.bootstrap.LocationPicker
29127  * @extends Roo.bootstrap.Component
29128  * Bootstrap LocationPicker class
29129  * @cfg {Number} latitude Position when init default 0
29130  * @cfg {Number} longitude Position when init default 0
29131  * @cfg {Number} zoom default 15
29132  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29133  * @cfg {Boolean} mapTypeControl default false
29134  * @cfg {Boolean} disableDoubleClickZoom default false
29135  * @cfg {Boolean} scrollwheel default true
29136  * @cfg {Boolean} streetViewControl default false
29137  * @cfg {Number} radius default 0
29138  * @cfg {String} locationName
29139  * @cfg {Boolean} draggable default true
29140  * @cfg {Boolean} enableAutocomplete default false
29141  * @cfg {Boolean} enableReverseGeocode default true
29142  * @cfg {String} markerTitle
29143  * 
29144  * @constructor
29145  * Create a new LocationPicker
29146  * @param {Object} config The config object
29147  */
29148
29149
29150 Roo.bootstrap.LocationPicker = function(config){
29151     
29152     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29153     
29154     this.addEvents({
29155         /**
29156          * @event initial
29157          * Fires when the picker initialized.
29158          * @param {Roo.bootstrap.LocationPicker} this
29159          * @param {Google Location} location
29160          */
29161         initial : true,
29162         /**
29163          * @event positionchanged
29164          * Fires when the picker position changed.
29165          * @param {Roo.bootstrap.LocationPicker} this
29166          * @param {Google Location} location
29167          */
29168         positionchanged : true,
29169         /**
29170          * @event resize
29171          * Fires when the map resize.
29172          * @param {Roo.bootstrap.LocationPicker} this
29173          */
29174         resize : true,
29175         /**
29176          * @event show
29177          * Fires when the map show.
29178          * @param {Roo.bootstrap.LocationPicker} this
29179          */
29180         show : true,
29181         /**
29182          * @event hide
29183          * Fires when the map hide.
29184          * @param {Roo.bootstrap.LocationPicker} this
29185          */
29186         hide : true,
29187         /**
29188          * @event mapClick
29189          * Fires when click the map.
29190          * @param {Roo.bootstrap.LocationPicker} this
29191          * @param {Map event} e
29192          */
29193         mapClick : true,
29194         /**
29195          * @event mapRightClick
29196          * Fires when right click the map.
29197          * @param {Roo.bootstrap.LocationPicker} this
29198          * @param {Map event} e
29199          */
29200         mapRightClick : true,
29201         /**
29202          * @event markerClick
29203          * Fires when click the marker.
29204          * @param {Roo.bootstrap.LocationPicker} this
29205          * @param {Map event} e
29206          */
29207         markerClick : true,
29208         /**
29209          * @event markerRightClick
29210          * Fires when right click the marker.
29211          * @param {Roo.bootstrap.LocationPicker} this
29212          * @param {Map event} e
29213          */
29214         markerRightClick : true,
29215         /**
29216          * @event OverlayViewDraw
29217          * Fires when OverlayView Draw
29218          * @param {Roo.bootstrap.LocationPicker} this
29219          */
29220         OverlayViewDraw : true,
29221         /**
29222          * @event OverlayViewOnAdd
29223          * Fires when OverlayView Draw
29224          * @param {Roo.bootstrap.LocationPicker} this
29225          */
29226         OverlayViewOnAdd : true,
29227         /**
29228          * @event OverlayViewOnRemove
29229          * Fires when OverlayView Draw
29230          * @param {Roo.bootstrap.LocationPicker} this
29231          */
29232         OverlayViewOnRemove : true,
29233         /**
29234          * @event OverlayViewShow
29235          * Fires when OverlayView Draw
29236          * @param {Roo.bootstrap.LocationPicker} this
29237          * @param {Pixel} cpx
29238          */
29239         OverlayViewShow : true,
29240         /**
29241          * @event OverlayViewHide
29242          * Fires when OverlayView Draw
29243          * @param {Roo.bootstrap.LocationPicker} this
29244          */
29245         OverlayViewHide : true,
29246         /**
29247          * @event loadexception
29248          * Fires when load google lib failed.
29249          * @param {Roo.bootstrap.LocationPicker} this
29250          */
29251         loadexception : true
29252     });
29253         
29254 };
29255
29256 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29257     
29258     gMapContext: false,
29259     
29260     latitude: 0,
29261     longitude: 0,
29262     zoom: 15,
29263     mapTypeId: false,
29264     mapTypeControl: false,
29265     disableDoubleClickZoom: false,
29266     scrollwheel: true,
29267     streetViewControl: false,
29268     radius: 0,
29269     locationName: '',
29270     draggable: true,
29271     enableAutocomplete: false,
29272     enableReverseGeocode: true,
29273     markerTitle: '',
29274     
29275     getAutoCreate: function()
29276     {
29277
29278         var cfg = {
29279             tag: 'div',
29280             cls: 'roo-location-picker'
29281         };
29282         
29283         return cfg
29284     },
29285     
29286     initEvents: function(ct, position)
29287     {       
29288         if(!this.el.getWidth() || this.isApplied()){
29289             return;
29290         }
29291         
29292         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29293         
29294         this.initial();
29295     },
29296     
29297     initial: function()
29298     {
29299         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29300             this.fireEvent('loadexception', this);
29301             return;
29302         }
29303         
29304         if(!this.mapTypeId){
29305             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29306         }
29307         
29308         this.gMapContext = this.GMapContext();
29309         
29310         this.initOverlayView();
29311         
29312         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29313         
29314         var _this = this;
29315                 
29316         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29317             _this.setPosition(_this.gMapContext.marker.position);
29318         });
29319         
29320         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29321             _this.fireEvent('mapClick', this, event);
29322             
29323         });
29324
29325         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29326             _this.fireEvent('mapRightClick', this, event);
29327             
29328         });
29329         
29330         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29331             _this.fireEvent('markerClick', this, event);
29332             
29333         });
29334
29335         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29336             _this.fireEvent('markerRightClick', this, event);
29337             
29338         });
29339         
29340         this.setPosition(this.gMapContext.location);
29341         
29342         this.fireEvent('initial', this, this.gMapContext.location);
29343     },
29344     
29345     initOverlayView: function()
29346     {
29347         var _this = this;
29348         
29349         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29350             
29351             draw: function()
29352             {
29353                 _this.fireEvent('OverlayViewDraw', _this);
29354             },
29355             
29356             onAdd: function()
29357             {
29358                 _this.fireEvent('OverlayViewOnAdd', _this);
29359             },
29360             
29361             onRemove: function()
29362             {
29363                 _this.fireEvent('OverlayViewOnRemove', _this);
29364             },
29365             
29366             show: function(cpx)
29367             {
29368                 _this.fireEvent('OverlayViewShow', _this, cpx);
29369             },
29370             
29371             hide: function()
29372             {
29373                 _this.fireEvent('OverlayViewHide', _this);
29374             }
29375             
29376         });
29377     },
29378     
29379     fromLatLngToContainerPixel: function(event)
29380     {
29381         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29382     },
29383     
29384     isApplied: function() 
29385     {
29386         return this.getGmapContext() == false ? false : true;
29387     },
29388     
29389     getGmapContext: function() 
29390     {
29391         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29392     },
29393     
29394     GMapContext: function() 
29395     {
29396         var position = new google.maps.LatLng(this.latitude, this.longitude);
29397         
29398         var _map = new google.maps.Map(this.el.dom, {
29399             center: position,
29400             zoom: this.zoom,
29401             mapTypeId: this.mapTypeId,
29402             mapTypeControl: this.mapTypeControl,
29403             disableDoubleClickZoom: this.disableDoubleClickZoom,
29404             scrollwheel: this.scrollwheel,
29405             streetViewControl: this.streetViewControl,
29406             locationName: this.locationName,
29407             draggable: this.draggable,
29408             enableAutocomplete: this.enableAutocomplete,
29409             enableReverseGeocode: this.enableReverseGeocode
29410         });
29411         
29412         var _marker = new google.maps.Marker({
29413             position: position,
29414             map: _map,
29415             title: this.markerTitle,
29416             draggable: this.draggable
29417         });
29418         
29419         return {
29420             map: _map,
29421             marker: _marker,
29422             circle: null,
29423             location: position,
29424             radius: this.radius,
29425             locationName: this.locationName,
29426             addressComponents: {
29427                 formatted_address: null,
29428                 addressLine1: null,
29429                 addressLine2: null,
29430                 streetName: null,
29431                 streetNumber: null,
29432                 city: null,
29433                 district: null,
29434                 state: null,
29435                 stateOrProvince: null
29436             },
29437             settings: this,
29438             domContainer: this.el.dom,
29439             geodecoder: new google.maps.Geocoder()
29440         };
29441     },
29442     
29443     drawCircle: function(center, radius, options) 
29444     {
29445         if (this.gMapContext.circle != null) {
29446             this.gMapContext.circle.setMap(null);
29447         }
29448         if (radius > 0) {
29449             radius *= 1;
29450             options = Roo.apply({}, options, {
29451                 strokeColor: "#0000FF",
29452                 strokeOpacity: .35,
29453                 strokeWeight: 2,
29454                 fillColor: "#0000FF",
29455                 fillOpacity: .2
29456             });
29457             
29458             options.map = this.gMapContext.map;
29459             options.radius = radius;
29460             options.center = center;
29461             this.gMapContext.circle = new google.maps.Circle(options);
29462             return this.gMapContext.circle;
29463         }
29464         
29465         return null;
29466     },
29467     
29468     setPosition: function(location) 
29469     {
29470         this.gMapContext.location = location;
29471         this.gMapContext.marker.setPosition(location);
29472         this.gMapContext.map.panTo(location);
29473         this.drawCircle(location, this.gMapContext.radius, {});
29474         
29475         var _this = this;
29476         
29477         if (this.gMapContext.settings.enableReverseGeocode) {
29478             this.gMapContext.geodecoder.geocode({
29479                 latLng: this.gMapContext.location
29480             }, function(results, status) {
29481                 
29482                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29483                     _this.gMapContext.locationName = results[0].formatted_address;
29484                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29485                     
29486                     _this.fireEvent('positionchanged', this, location);
29487                 }
29488             });
29489             
29490             return;
29491         }
29492         
29493         this.fireEvent('positionchanged', this, location);
29494     },
29495     
29496     resize: function()
29497     {
29498         google.maps.event.trigger(this.gMapContext.map, "resize");
29499         
29500         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29501         
29502         this.fireEvent('resize', this);
29503     },
29504     
29505     setPositionByLatLng: function(latitude, longitude)
29506     {
29507         this.setPosition(new google.maps.LatLng(latitude, longitude));
29508     },
29509     
29510     getCurrentPosition: function() 
29511     {
29512         return {
29513             latitude: this.gMapContext.location.lat(),
29514             longitude: this.gMapContext.location.lng()
29515         };
29516     },
29517     
29518     getAddressName: function() 
29519     {
29520         return this.gMapContext.locationName;
29521     },
29522     
29523     getAddressComponents: function() 
29524     {
29525         return this.gMapContext.addressComponents;
29526     },
29527     
29528     address_component_from_google_geocode: function(address_components) 
29529     {
29530         var result = {};
29531         
29532         for (var i = 0; i < address_components.length; i++) {
29533             var component = address_components[i];
29534             if (component.types.indexOf("postal_code") >= 0) {
29535                 result.postalCode = component.short_name;
29536             } else if (component.types.indexOf("street_number") >= 0) {
29537                 result.streetNumber = component.short_name;
29538             } else if (component.types.indexOf("route") >= 0) {
29539                 result.streetName = component.short_name;
29540             } else if (component.types.indexOf("neighborhood") >= 0) {
29541                 result.city = component.short_name;
29542             } else if (component.types.indexOf("locality") >= 0) {
29543                 result.city = component.short_name;
29544             } else if (component.types.indexOf("sublocality") >= 0) {
29545                 result.district = component.short_name;
29546             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29547                 result.stateOrProvince = component.short_name;
29548             } else if (component.types.indexOf("country") >= 0) {
29549                 result.country = component.short_name;
29550             }
29551         }
29552         
29553         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29554         result.addressLine2 = "";
29555         return result;
29556     },
29557     
29558     setZoomLevel: function(zoom)
29559     {
29560         this.gMapContext.map.setZoom(zoom);
29561     },
29562     
29563     show: function()
29564     {
29565         if(!this.el){
29566             return;
29567         }
29568         
29569         this.el.show();
29570         
29571         this.resize();
29572         
29573         this.fireEvent('show', this);
29574     },
29575     
29576     hide: function()
29577     {
29578         if(!this.el){
29579             return;
29580         }
29581         
29582         this.el.hide();
29583         
29584         this.fireEvent('hide', this);
29585     }
29586     
29587 });
29588
29589 Roo.apply(Roo.bootstrap.LocationPicker, {
29590     
29591     OverlayView : function(map, options)
29592     {
29593         options = options || {};
29594         
29595         this.setMap(map);
29596     }
29597     
29598     
29599 });/**
29600  * @class Roo.bootstrap.Alert
29601  * @extends Roo.bootstrap.Component
29602  * Bootstrap Alert class - shows an alert area box
29603  * eg
29604  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29605   Enter a valid email address
29606 </div>
29607  * @licence LGPL
29608  * @cfg {String} title The title of alert
29609  * @cfg {String} html The content of alert
29610  * @cfg {String} weight (  success | info | warning | danger )
29611  * @cfg {String} faicon font-awesomeicon
29612  * 
29613  * @constructor
29614  * Create a new alert
29615  * @param {Object} config The config object
29616  */
29617
29618
29619 Roo.bootstrap.Alert = function(config){
29620     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29621     
29622 };
29623
29624 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29625     
29626     title: '',
29627     html: '',
29628     weight: false,
29629     faicon: false,
29630     
29631     getAutoCreate : function()
29632     {
29633         
29634         var cfg = {
29635             tag : 'div',
29636             cls : 'alert',
29637             cn : [
29638                 {
29639                     tag : 'i',
29640                     cls : 'roo-alert-icon'
29641                     
29642                 },
29643                 {
29644                     tag : 'b',
29645                     cls : 'roo-alert-title',
29646                     html : this.title
29647                 },
29648                 {
29649                     tag : 'span',
29650                     cls : 'roo-alert-text',
29651                     html : this.html
29652                 }
29653             ]
29654         };
29655         
29656         if(this.faicon){
29657             cfg.cn[0].cls += ' fa ' + this.faicon;
29658         }
29659         
29660         if(this.weight){
29661             cfg.cls += ' alert-' + this.weight;
29662         }
29663         
29664         return cfg;
29665     },
29666     
29667     initEvents: function() 
29668     {
29669         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29670     },
29671     
29672     setTitle : function(str)
29673     {
29674         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29675     },
29676     
29677     setText : function(str)
29678     {
29679         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29680     },
29681     
29682     setWeight : function(weight)
29683     {
29684         if(this.weight){
29685             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29686         }
29687         
29688         this.weight = weight;
29689         
29690         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29691     },
29692     
29693     setIcon : function(icon)
29694     {
29695         if(this.faicon){
29696             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29697         }
29698         
29699         this.faicon = icon;
29700         
29701         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29702     },
29703     
29704     hide: function() 
29705     {
29706         this.el.hide();   
29707     },
29708     
29709     show: function() 
29710     {  
29711         this.el.show();   
29712     }
29713     
29714 });
29715
29716  
29717 /*
29718 * Licence: LGPL
29719 */
29720
29721 /**
29722  * @class Roo.bootstrap.UploadCropbox
29723  * @extends Roo.bootstrap.Component
29724  * Bootstrap UploadCropbox class
29725  * @cfg {String} emptyText show when image has been loaded
29726  * @cfg {String} rotateNotify show when image too small to rotate
29727  * @cfg {Number} errorTimeout default 3000
29728  * @cfg {Number} minWidth default 300
29729  * @cfg {Number} minHeight default 300
29730  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29731  * @cfg {Boolean} isDocument (true|false) default false
29732  * @cfg {String} url action url
29733  * @cfg {String} paramName default 'imageUpload'
29734  * @cfg {String} method default POST
29735  * @cfg {Boolean} loadMask (true|false) default true
29736  * @cfg {Boolean} loadingText default 'Loading...'
29737  * 
29738  * @constructor
29739  * Create a new UploadCropbox
29740  * @param {Object} config The config object
29741  */
29742
29743 Roo.bootstrap.UploadCropbox = function(config){
29744     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29745     
29746     this.addEvents({
29747         /**
29748          * @event beforeselectfile
29749          * Fire before select file
29750          * @param {Roo.bootstrap.UploadCropbox} this
29751          */
29752         "beforeselectfile" : true,
29753         /**
29754          * @event initial
29755          * Fire after initEvent
29756          * @param {Roo.bootstrap.UploadCropbox} this
29757          */
29758         "initial" : true,
29759         /**
29760          * @event crop
29761          * Fire after initEvent
29762          * @param {Roo.bootstrap.UploadCropbox} this
29763          * @param {String} data
29764          */
29765         "crop" : true,
29766         /**
29767          * @event prepare
29768          * Fire when preparing the file data
29769          * @param {Roo.bootstrap.UploadCropbox} this
29770          * @param {Object} file
29771          */
29772         "prepare" : true,
29773         /**
29774          * @event exception
29775          * Fire when get exception
29776          * @param {Roo.bootstrap.UploadCropbox} this
29777          * @param {XMLHttpRequest} xhr
29778          */
29779         "exception" : true,
29780         /**
29781          * @event beforeloadcanvas
29782          * Fire before load the canvas
29783          * @param {Roo.bootstrap.UploadCropbox} this
29784          * @param {String} src
29785          */
29786         "beforeloadcanvas" : true,
29787         /**
29788          * @event trash
29789          * Fire when trash image
29790          * @param {Roo.bootstrap.UploadCropbox} this
29791          */
29792         "trash" : true,
29793         /**
29794          * @event download
29795          * Fire when download the image
29796          * @param {Roo.bootstrap.UploadCropbox} this
29797          */
29798         "download" : true,
29799         /**
29800          * @event footerbuttonclick
29801          * Fire when footerbuttonclick
29802          * @param {Roo.bootstrap.UploadCropbox} this
29803          * @param {String} type
29804          */
29805         "footerbuttonclick" : true,
29806         /**
29807          * @event resize
29808          * Fire when resize
29809          * @param {Roo.bootstrap.UploadCropbox} this
29810          */
29811         "resize" : true,
29812         /**
29813          * @event rotate
29814          * Fire when rotate the image
29815          * @param {Roo.bootstrap.UploadCropbox} this
29816          * @param {String} pos
29817          */
29818         "rotate" : true,
29819         /**
29820          * @event inspect
29821          * Fire when inspect the file
29822          * @param {Roo.bootstrap.UploadCropbox} this
29823          * @param {Object} file
29824          */
29825         "inspect" : true,
29826         /**
29827          * @event upload
29828          * Fire when xhr upload the file
29829          * @param {Roo.bootstrap.UploadCropbox} this
29830          * @param {Object} data
29831          */
29832         "upload" : true,
29833         /**
29834          * @event arrange
29835          * Fire when arrange the file data
29836          * @param {Roo.bootstrap.UploadCropbox} this
29837          * @param {Object} formData
29838          */
29839         "arrange" : true
29840     });
29841     
29842     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29843 };
29844
29845 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29846     
29847     emptyText : 'Click to upload image',
29848     rotateNotify : 'Image is too small to rotate',
29849     errorTimeout : 3000,
29850     scale : 0,
29851     baseScale : 1,
29852     rotate : 0,
29853     dragable : false,
29854     pinching : false,
29855     mouseX : 0,
29856     mouseY : 0,
29857     cropData : false,
29858     minWidth : 300,
29859     minHeight : 300,
29860     file : false,
29861     exif : {},
29862     baseRotate : 1,
29863     cropType : 'image/jpeg',
29864     buttons : false,
29865     canvasLoaded : false,
29866     isDocument : false,
29867     method : 'POST',
29868     paramName : 'imageUpload',
29869     loadMask : true,
29870     loadingText : 'Loading...',
29871     maskEl : false,
29872     
29873     getAutoCreate : function()
29874     {
29875         var cfg = {
29876             tag : 'div',
29877             cls : 'roo-upload-cropbox',
29878             cn : [
29879                 {
29880                     tag : 'input',
29881                     cls : 'roo-upload-cropbox-selector',
29882                     type : 'file'
29883                 },
29884                 {
29885                     tag : 'div',
29886                     cls : 'roo-upload-cropbox-body',
29887                     style : 'cursor:pointer',
29888                     cn : [
29889                         {
29890                             tag : 'div',
29891                             cls : 'roo-upload-cropbox-preview'
29892                         },
29893                         {
29894                             tag : 'div',
29895                             cls : 'roo-upload-cropbox-thumb'
29896                         },
29897                         {
29898                             tag : 'div',
29899                             cls : 'roo-upload-cropbox-empty-notify',
29900                             html : this.emptyText
29901                         },
29902                         {
29903                             tag : 'div',
29904                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29905                             html : this.rotateNotify
29906                         }
29907                     ]
29908                 },
29909                 {
29910                     tag : 'div',
29911                     cls : 'roo-upload-cropbox-footer',
29912                     cn : {
29913                         tag : 'div',
29914                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29915                         cn : []
29916                     }
29917                 }
29918             ]
29919         };
29920         
29921         return cfg;
29922     },
29923     
29924     onRender : function(ct, position)
29925     {
29926         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29927         
29928         if (this.buttons.length) {
29929             
29930             Roo.each(this.buttons, function(bb) {
29931                 
29932                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29933                 
29934                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29935                 
29936             }, this);
29937         }
29938         
29939         if(this.loadMask){
29940             this.maskEl = this.el;
29941         }
29942     },
29943     
29944     initEvents : function()
29945     {
29946         this.urlAPI = (window.createObjectURL && window) || 
29947                                 (window.URL && URL.revokeObjectURL && URL) || 
29948                                 (window.webkitURL && webkitURL);
29949                         
29950         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29951         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29952         
29953         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29954         this.selectorEl.hide();
29955         
29956         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29957         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29958         
29959         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29960         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29961         this.thumbEl.hide();
29962         
29963         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29964         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29965         
29966         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29967         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29968         this.errorEl.hide();
29969         
29970         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29971         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29972         this.footerEl.hide();
29973         
29974         this.setThumbBoxSize();
29975         
29976         this.bind();
29977         
29978         this.resize();
29979         
29980         this.fireEvent('initial', this);
29981     },
29982
29983     bind : function()
29984     {
29985         var _this = this;
29986         
29987         window.addEventListener("resize", function() { _this.resize(); } );
29988         
29989         this.bodyEl.on('click', this.beforeSelectFile, this);
29990         
29991         if(Roo.isTouch){
29992             this.bodyEl.on('touchstart', this.onTouchStart, this);
29993             this.bodyEl.on('touchmove', this.onTouchMove, this);
29994             this.bodyEl.on('touchend', this.onTouchEnd, this);
29995         }
29996         
29997         if(!Roo.isTouch){
29998             this.bodyEl.on('mousedown', this.onMouseDown, this);
29999             this.bodyEl.on('mousemove', this.onMouseMove, this);
30000             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30001             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30002             Roo.get(document).on('mouseup', this.onMouseUp, this);
30003         }
30004         
30005         this.selectorEl.on('change', this.onFileSelected, this);
30006     },
30007     
30008     reset : function()
30009     {    
30010         this.scale = 0;
30011         this.baseScale = 1;
30012         this.rotate = 0;
30013         this.baseRotate = 1;
30014         this.dragable = false;
30015         this.pinching = false;
30016         this.mouseX = 0;
30017         this.mouseY = 0;
30018         this.cropData = false;
30019         this.notifyEl.dom.innerHTML = this.emptyText;
30020         
30021         this.selectorEl.dom.value = '';
30022         
30023     },
30024     
30025     resize : function()
30026     {
30027         if(this.fireEvent('resize', this) != false){
30028             this.setThumbBoxPosition();
30029             this.setCanvasPosition();
30030         }
30031     },
30032     
30033     onFooterButtonClick : function(e, el, o, type)
30034     {
30035         switch (type) {
30036             case 'rotate-left' :
30037                 this.onRotateLeft(e);
30038                 break;
30039             case 'rotate-right' :
30040                 this.onRotateRight(e);
30041                 break;
30042             case 'picture' :
30043                 this.beforeSelectFile(e);
30044                 break;
30045             case 'trash' :
30046                 this.trash(e);
30047                 break;
30048             case 'crop' :
30049                 this.crop(e);
30050                 break;
30051             case 'download' :
30052                 this.download(e);
30053                 break;
30054             default :
30055                 break;
30056         }
30057         
30058         this.fireEvent('footerbuttonclick', this, type);
30059     },
30060     
30061     beforeSelectFile : function(e)
30062     {
30063         e.preventDefault();
30064         
30065         if(this.fireEvent('beforeselectfile', this) != false){
30066             this.selectorEl.dom.click();
30067         }
30068     },
30069     
30070     onFileSelected : function(e)
30071     {
30072         e.preventDefault();
30073         
30074         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30075             return;
30076         }
30077         
30078         var file = this.selectorEl.dom.files[0];
30079         
30080         if(this.fireEvent('inspect', this, file) != false){
30081             this.prepare(file);
30082         }
30083         
30084     },
30085     
30086     trash : function(e)
30087     {
30088         this.fireEvent('trash', this);
30089     },
30090     
30091     download : function(e)
30092     {
30093         this.fireEvent('download', this);
30094     },
30095     
30096     loadCanvas : function(src)
30097     {   
30098         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30099             
30100             this.reset();
30101             
30102             this.imageEl = document.createElement('img');
30103             
30104             var _this = this;
30105             
30106             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30107             
30108             this.imageEl.src = src;
30109         }
30110     },
30111     
30112     onLoadCanvas : function()
30113     {   
30114         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30115         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30116         
30117         this.bodyEl.un('click', this.beforeSelectFile, this);
30118         
30119         this.notifyEl.hide();
30120         this.thumbEl.show();
30121         this.footerEl.show();
30122         
30123         this.baseRotateLevel();
30124         
30125         if(this.isDocument){
30126             this.setThumbBoxSize();
30127         }
30128         
30129         this.setThumbBoxPosition();
30130         
30131         this.baseScaleLevel();
30132         
30133         this.draw();
30134         
30135         this.resize();
30136         
30137         this.canvasLoaded = true;
30138         
30139         if(this.loadMask){
30140             this.maskEl.unmask();
30141         }
30142         
30143     },
30144     
30145     setCanvasPosition : function()
30146     {   
30147         if(!this.canvasEl){
30148             return;
30149         }
30150         
30151         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30152         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30153         
30154         this.previewEl.setLeft(pw);
30155         this.previewEl.setTop(ph);
30156         
30157     },
30158     
30159     onMouseDown : function(e)
30160     {   
30161         e.stopEvent();
30162         
30163         this.dragable = true;
30164         this.pinching = false;
30165         
30166         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30167             this.dragable = false;
30168             return;
30169         }
30170         
30171         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30172         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30173         
30174     },
30175     
30176     onMouseMove : function(e)
30177     {   
30178         e.stopEvent();
30179         
30180         if(!this.canvasLoaded){
30181             return;
30182         }
30183         
30184         if (!this.dragable){
30185             return;
30186         }
30187         
30188         var minX = Math.ceil(this.thumbEl.getLeft(true));
30189         var minY = Math.ceil(this.thumbEl.getTop(true));
30190         
30191         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30192         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30193         
30194         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30195         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30196         
30197         x = x - this.mouseX;
30198         y = y - this.mouseY;
30199         
30200         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30201         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30202         
30203         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30204         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30205         
30206         this.previewEl.setLeft(bgX);
30207         this.previewEl.setTop(bgY);
30208         
30209         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30210         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30211     },
30212     
30213     onMouseUp : function(e)
30214     {   
30215         e.stopEvent();
30216         
30217         this.dragable = false;
30218     },
30219     
30220     onMouseWheel : function(e)
30221     {   
30222         e.stopEvent();
30223         
30224         this.startScale = this.scale;
30225         
30226         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30227         
30228         if(!this.zoomable()){
30229             this.scale = this.startScale;
30230             return;
30231         }
30232         
30233         this.draw();
30234         
30235         return;
30236     },
30237     
30238     zoomable : function()
30239     {
30240         var minScale = this.thumbEl.getWidth() / this.minWidth;
30241         
30242         if(this.minWidth < this.minHeight){
30243             minScale = this.thumbEl.getHeight() / this.minHeight;
30244         }
30245         
30246         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30247         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30248         
30249         if(
30250                 this.isDocument &&
30251                 (this.rotate == 0 || this.rotate == 180) && 
30252                 (
30253                     width > this.imageEl.OriginWidth || 
30254                     height > this.imageEl.OriginHeight ||
30255                     (width < this.minWidth && height < this.minHeight)
30256                 )
30257         ){
30258             return false;
30259         }
30260         
30261         if(
30262                 this.isDocument &&
30263                 (this.rotate == 90 || this.rotate == 270) && 
30264                 (
30265                     width > this.imageEl.OriginWidth || 
30266                     height > this.imageEl.OriginHeight ||
30267                     (width < this.minHeight && height < this.minWidth)
30268                 )
30269         ){
30270             return false;
30271         }
30272         
30273         if(
30274                 !this.isDocument &&
30275                 (this.rotate == 0 || this.rotate == 180) && 
30276                 (
30277                     width < this.minWidth || 
30278                     width > this.imageEl.OriginWidth || 
30279                     height < this.minHeight || 
30280                     height > this.imageEl.OriginHeight
30281                 )
30282         ){
30283             return false;
30284         }
30285         
30286         if(
30287                 !this.isDocument &&
30288                 (this.rotate == 90 || this.rotate == 270) && 
30289                 (
30290                     width < this.minHeight || 
30291                     width > this.imageEl.OriginWidth || 
30292                     height < this.minWidth || 
30293                     height > this.imageEl.OriginHeight
30294                 )
30295         ){
30296             return false;
30297         }
30298         
30299         return true;
30300         
30301     },
30302     
30303     onRotateLeft : function(e)
30304     {   
30305         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30306             
30307             var minScale = this.thumbEl.getWidth() / this.minWidth;
30308             
30309             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30310             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30311             
30312             this.startScale = this.scale;
30313             
30314             while (this.getScaleLevel() < minScale){
30315             
30316                 this.scale = this.scale + 1;
30317                 
30318                 if(!this.zoomable()){
30319                     break;
30320                 }
30321                 
30322                 if(
30323                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30324                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30325                 ){
30326                     continue;
30327                 }
30328                 
30329                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30330
30331                 this.draw();
30332                 
30333                 return;
30334             }
30335             
30336             this.scale = this.startScale;
30337             
30338             this.onRotateFail();
30339             
30340             return false;
30341         }
30342         
30343         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30344
30345         if(this.isDocument){
30346             this.setThumbBoxSize();
30347             this.setThumbBoxPosition();
30348             this.setCanvasPosition();
30349         }
30350         
30351         this.draw();
30352         
30353         this.fireEvent('rotate', this, 'left');
30354         
30355     },
30356     
30357     onRotateRight : function(e)
30358     {
30359         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30360             
30361             var minScale = this.thumbEl.getWidth() / this.minWidth;
30362         
30363             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30364             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30365             
30366             this.startScale = this.scale;
30367             
30368             while (this.getScaleLevel() < minScale){
30369             
30370                 this.scale = this.scale + 1;
30371                 
30372                 if(!this.zoomable()){
30373                     break;
30374                 }
30375                 
30376                 if(
30377                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30378                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30379                 ){
30380                     continue;
30381                 }
30382                 
30383                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30384
30385                 this.draw();
30386                 
30387                 return;
30388             }
30389             
30390             this.scale = this.startScale;
30391             
30392             this.onRotateFail();
30393             
30394             return false;
30395         }
30396         
30397         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30398
30399         if(this.isDocument){
30400             this.setThumbBoxSize();
30401             this.setThumbBoxPosition();
30402             this.setCanvasPosition();
30403         }
30404         
30405         this.draw();
30406         
30407         this.fireEvent('rotate', this, 'right');
30408     },
30409     
30410     onRotateFail : function()
30411     {
30412         this.errorEl.show(true);
30413         
30414         var _this = this;
30415         
30416         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30417     },
30418     
30419     draw : function()
30420     {
30421         this.previewEl.dom.innerHTML = '';
30422         
30423         var canvasEl = document.createElement("canvas");
30424         
30425         var contextEl = canvasEl.getContext("2d");
30426         
30427         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30428         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30429         var center = this.imageEl.OriginWidth / 2;
30430         
30431         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30432             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30433             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30434             center = this.imageEl.OriginHeight / 2;
30435         }
30436         
30437         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30438         
30439         contextEl.translate(center, center);
30440         contextEl.rotate(this.rotate * Math.PI / 180);
30441
30442         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30443         
30444         this.canvasEl = document.createElement("canvas");
30445         
30446         this.contextEl = this.canvasEl.getContext("2d");
30447         
30448         switch (this.rotate) {
30449             case 0 :
30450                 
30451                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30452                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30453                 
30454                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30455                 
30456                 break;
30457             case 90 : 
30458                 
30459                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30460                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30461                 
30462                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30463                     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);
30464                     break;
30465                 }
30466                 
30467                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30468                 
30469                 break;
30470             case 180 :
30471                 
30472                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30473                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30474                 
30475                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30476                     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);
30477                     break;
30478                 }
30479                 
30480                 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);
30481                 
30482                 break;
30483             case 270 :
30484                 
30485                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30486                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30487         
30488                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30489                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30490                     break;
30491                 }
30492                 
30493                 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);
30494                 
30495                 break;
30496             default : 
30497                 break;
30498         }
30499         
30500         this.previewEl.appendChild(this.canvasEl);
30501         
30502         this.setCanvasPosition();
30503     },
30504     
30505     crop : function()
30506     {
30507         if(!this.canvasLoaded){
30508             return;
30509         }
30510         
30511         var imageCanvas = document.createElement("canvas");
30512         
30513         var imageContext = imageCanvas.getContext("2d");
30514         
30515         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30516         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30517         
30518         var center = imageCanvas.width / 2;
30519         
30520         imageContext.translate(center, center);
30521         
30522         imageContext.rotate(this.rotate * Math.PI / 180);
30523         
30524         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30525         
30526         var canvas = document.createElement("canvas");
30527         
30528         var context = canvas.getContext("2d");
30529                 
30530         canvas.width = this.minWidth;
30531         canvas.height = this.minHeight;
30532
30533         switch (this.rotate) {
30534             case 0 :
30535                 
30536                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30537                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30538                 
30539                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30540                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30541                 
30542                 var targetWidth = this.minWidth - 2 * x;
30543                 var targetHeight = this.minHeight - 2 * y;
30544                 
30545                 var scale = 1;
30546                 
30547                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30548                     scale = targetWidth / width;
30549                 }
30550                 
30551                 if(x > 0 && y == 0){
30552                     scale = targetHeight / height;
30553                 }
30554                 
30555                 if(x > 0 && y > 0){
30556                     scale = targetWidth / width;
30557                     
30558                     if(width < height){
30559                         scale = targetHeight / height;
30560                     }
30561                 }
30562                 
30563                 context.scale(scale, scale);
30564                 
30565                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30566                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30567
30568                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30569                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30570
30571                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30572                 
30573                 break;
30574             case 90 : 
30575                 
30576                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30577                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30578                 
30579                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30580                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30581                 
30582                 var targetWidth = this.minWidth - 2 * x;
30583                 var targetHeight = this.minHeight - 2 * y;
30584                 
30585                 var scale = 1;
30586                 
30587                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30588                     scale = targetWidth / width;
30589                 }
30590                 
30591                 if(x > 0 && y == 0){
30592                     scale = targetHeight / height;
30593                 }
30594                 
30595                 if(x > 0 && y > 0){
30596                     scale = targetWidth / width;
30597                     
30598                     if(width < height){
30599                         scale = targetHeight / height;
30600                     }
30601                 }
30602                 
30603                 context.scale(scale, scale);
30604                 
30605                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30606                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30607
30608                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30609                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30610                 
30611                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30612                 
30613                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30614                 
30615                 break;
30616             case 180 :
30617                 
30618                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30619                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30620                 
30621                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30622                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30623                 
30624                 var targetWidth = this.minWidth - 2 * x;
30625                 var targetHeight = this.minHeight - 2 * y;
30626                 
30627                 var scale = 1;
30628                 
30629                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30630                     scale = targetWidth / width;
30631                 }
30632                 
30633                 if(x > 0 && y == 0){
30634                     scale = targetHeight / height;
30635                 }
30636                 
30637                 if(x > 0 && y > 0){
30638                     scale = targetWidth / width;
30639                     
30640                     if(width < height){
30641                         scale = targetHeight / height;
30642                     }
30643                 }
30644                 
30645                 context.scale(scale, scale);
30646                 
30647                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30648                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30649
30650                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30651                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30652
30653                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30654                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30655                 
30656                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30657                 
30658                 break;
30659             case 270 :
30660                 
30661                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30662                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30663                 
30664                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30665                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30666                 
30667                 var targetWidth = this.minWidth - 2 * x;
30668                 var targetHeight = this.minHeight - 2 * y;
30669                 
30670                 var scale = 1;
30671                 
30672                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30673                     scale = targetWidth / width;
30674                 }
30675                 
30676                 if(x > 0 && y == 0){
30677                     scale = targetHeight / height;
30678                 }
30679                 
30680                 if(x > 0 && y > 0){
30681                     scale = targetWidth / width;
30682                     
30683                     if(width < height){
30684                         scale = targetHeight / height;
30685                     }
30686                 }
30687                 
30688                 context.scale(scale, scale);
30689                 
30690                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30691                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30692
30693                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30694                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30695                 
30696                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30697                 
30698                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30699                 
30700                 break;
30701             default : 
30702                 break;
30703         }
30704         
30705         this.cropData = canvas.toDataURL(this.cropType);
30706         
30707         if(this.fireEvent('crop', this, this.cropData) !== false){
30708             this.process(this.file, this.cropData);
30709         }
30710         
30711         return;
30712         
30713     },
30714     
30715     setThumbBoxSize : function()
30716     {
30717         var width, height;
30718         
30719         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30720             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30721             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30722             
30723             this.minWidth = width;
30724             this.minHeight = height;
30725             
30726             if(this.rotate == 90 || this.rotate == 270){
30727                 this.minWidth = height;
30728                 this.minHeight = width;
30729             }
30730         }
30731         
30732         height = 300;
30733         width = Math.ceil(this.minWidth * height / this.minHeight);
30734         
30735         if(this.minWidth > this.minHeight){
30736             width = 300;
30737             height = Math.ceil(this.minHeight * width / this.minWidth);
30738         }
30739         
30740         this.thumbEl.setStyle({
30741             width : width + 'px',
30742             height : height + 'px'
30743         });
30744
30745         return;
30746             
30747     },
30748     
30749     setThumbBoxPosition : function()
30750     {
30751         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30752         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30753         
30754         this.thumbEl.setLeft(x);
30755         this.thumbEl.setTop(y);
30756         
30757     },
30758     
30759     baseRotateLevel : function()
30760     {
30761         this.baseRotate = 1;
30762         
30763         if(
30764                 typeof(this.exif) != 'undefined' &&
30765                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30766                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30767         ){
30768             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30769         }
30770         
30771         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30772         
30773     },
30774     
30775     baseScaleLevel : function()
30776     {
30777         var width, height;
30778         
30779         if(this.isDocument){
30780             
30781             if(this.baseRotate == 6 || this.baseRotate == 8){
30782             
30783                 height = this.thumbEl.getHeight();
30784                 this.baseScale = height / this.imageEl.OriginWidth;
30785
30786                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30787                     width = this.thumbEl.getWidth();
30788                     this.baseScale = width / this.imageEl.OriginHeight;
30789                 }
30790
30791                 return;
30792             }
30793
30794             height = this.thumbEl.getHeight();
30795             this.baseScale = height / this.imageEl.OriginHeight;
30796
30797             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30798                 width = this.thumbEl.getWidth();
30799                 this.baseScale = width / this.imageEl.OriginWidth;
30800             }
30801
30802             return;
30803         }
30804         
30805         if(this.baseRotate == 6 || this.baseRotate == 8){
30806             
30807             width = this.thumbEl.getHeight();
30808             this.baseScale = width / this.imageEl.OriginHeight;
30809             
30810             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30811                 height = this.thumbEl.getWidth();
30812                 this.baseScale = height / this.imageEl.OriginHeight;
30813             }
30814             
30815             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30816                 height = this.thumbEl.getWidth();
30817                 this.baseScale = height / this.imageEl.OriginHeight;
30818                 
30819                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30820                     width = this.thumbEl.getHeight();
30821                     this.baseScale = width / this.imageEl.OriginWidth;
30822                 }
30823             }
30824             
30825             return;
30826         }
30827         
30828         width = this.thumbEl.getWidth();
30829         this.baseScale = width / this.imageEl.OriginWidth;
30830         
30831         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30832             height = this.thumbEl.getHeight();
30833             this.baseScale = height / this.imageEl.OriginHeight;
30834         }
30835         
30836         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30837             
30838             height = this.thumbEl.getHeight();
30839             this.baseScale = height / this.imageEl.OriginHeight;
30840             
30841             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30842                 width = this.thumbEl.getWidth();
30843                 this.baseScale = width / this.imageEl.OriginWidth;
30844             }
30845             
30846         }
30847         
30848         return;
30849     },
30850     
30851     getScaleLevel : function()
30852     {
30853         return this.baseScale * Math.pow(1.1, this.scale);
30854     },
30855     
30856     onTouchStart : function(e)
30857     {
30858         if(!this.canvasLoaded){
30859             this.beforeSelectFile(e);
30860             return;
30861         }
30862         
30863         var touches = e.browserEvent.touches;
30864         
30865         if(!touches){
30866             return;
30867         }
30868         
30869         if(touches.length == 1){
30870             this.onMouseDown(e);
30871             return;
30872         }
30873         
30874         if(touches.length != 2){
30875             return;
30876         }
30877         
30878         var coords = [];
30879         
30880         for(var i = 0, finger; finger = touches[i]; i++){
30881             coords.push(finger.pageX, finger.pageY);
30882         }
30883         
30884         var x = Math.pow(coords[0] - coords[2], 2);
30885         var y = Math.pow(coords[1] - coords[3], 2);
30886         
30887         this.startDistance = Math.sqrt(x + y);
30888         
30889         this.startScale = this.scale;
30890         
30891         this.pinching = true;
30892         this.dragable = false;
30893         
30894     },
30895     
30896     onTouchMove : function(e)
30897     {
30898         if(!this.pinching && !this.dragable){
30899             return;
30900         }
30901         
30902         var touches = e.browserEvent.touches;
30903         
30904         if(!touches){
30905             return;
30906         }
30907         
30908         if(this.dragable){
30909             this.onMouseMove(e);
30910             return;
30911         }
30912         
30913         var coords = [];
30914         
30915         for(var i = 0, finger; finger = touches[i]; i++){
30916             coords.push(finger.pageX, finger.pageY);
30917         }
30918         
30919         var x = Math.pow(coords[0] - coords[2], 2);
30920         var y = Math.pow(coords[1] - coords[3], 2);
30921         
30922         this.endDistance = Math.sqrt(x + y);
30923         
30924         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30925         
30926         if(!this.zoomable()){
30927             this.scale = this.startScale;
30928             return;
30929         }
30930         
30931         this.draw();
30932         
30933     },
30934     
30935     onTouchEnd : function(e)
30936     {
30937         this.pinching = false;
30938         this.dragable = false;
30939         
30940     },
30941     
30942     process : function(file, crop)
30943     {
30944         if(this.loadMask){
30945             this.maskEl.mask(this.loadingText);
30946         }
30947         
30948         this.xhr = new XMLHttpRequest();
30949         
30950         file.xhr = this.xhr;
30951
30952         this.xhr.open(this.method, this.url, true);
30953         
30954         var headers = {
30955             "Accept": "application/json",
30956             "Cache-Control": "no-cache",
30957             "X-Requested-With": "XMLHttpRequest"
30958         };
30959         
30960         for (var headerName in headers) {
30961             var headerValue = headers[headerName];
30962             if (headerValue) {
30963                 this.xhr.setRequestHeader(headerName, headerValue);
30964             }
30965         }
30966         
30967         var _this = this;
30968         
30969         this.xhr.onload = function()
30970         {
30971             _this.xhrOnLoad(_this.xhr);
30972         }
30973         
30974         this.xhr.onerror = function()
30975         {
30976             _this.xhrOnError(_this.xhr);
30977         }
30978         
30979         var formData = new FormData();
30980
30981         formData.append('returnHTML', 'NO');
30982         
30983         if(crop){
30984             formData.append('crop', crop);
30985         }
30986         
30987         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30988             formData.append(this.paramName, file, file.name);
30989         }
30990         
30991         if(typeof(file.filename) != 'undefined'){
30992             formData.append('filename', file.filename);
30993         }
30994         
30995         if(typeof(file.mimetype) != 'undefined'){
30996             formData.append('mimetype', file.mimetype);
30997         }
30998         
30999         if(this.fireEvent('arrange', this, formData) != false){
31000             this.xhr.send(formData);
31001         };
31002     },
31003     
31004     xhrOnLoad : function(xhr)
31005     {
31006         if(this.loadMask){
31007             this.maskEl.unmask();
31008         }
31009         
31010         if (xhr.readyState !== 4) {
31011             this.fireEvent('exception', this, xhr);
31012             return;
31013         }
31014
31015         var response = Roo.decode(xhr.responseText);
31016         
31017         if(!response.success){
31018             this.fireEvent('exception', this, xhr);
31019             return;
31020         }
31021         
31022         var response = Roo.decode(xhr.responseText);
31023         
31024         this.fireEvent('upload', this, response);
31025         
31026     },
31027     
31028     xhrOnError : function()
31029     {
31030         if(this.loadMask){
31031             this.maskEl.unmask();
31032         }
31033         
31034         Roo.log('xhr on error');
31035         
31036         var response = Roo.decode(xhr.responseText);
31037           
31038         Roo.log(response);
31039         
31040     },
31041     
31042     prepare : function(file)
31043     {   
31044         if(this.loadMask){
31045             this.maskEl.mask(this.loadingText);
31046         }
31047         
31048         this.file = false;
31049         this.exif = {};
31050         
31051         if(typeof(file) === 'string'){
31052             this.loadCanvas(file);
31053             return;
31054         }
31055         
31056         if(!file || !this.urlAPI){
31057             return;
31058         }
31059         
31060         this.file = file;
31061         this.cropType = file.type;
31062         
31063         var _this = this;
31064         
31065         if(this.fireEvent('prepare', this, this.file) != false){
31066             
31067             var reader = new FileReader();
31068             
31069             reader.onload = function (e) {
31070                 if (e.target.error) {
31071                     Roo.log(e.target.error);
31072                     return;
31073                 }
31074                 
31075                 var buffer = e.target.result,
31076                     dataView = new DataView(buffer),
31077                     offset = 2,
31078                     maxOffset = dataView.byteLength - 4,
31079                     markerBytes,
31080                     markerLength;
31081                 
31082                 if (dataView.getUint16(0) === 0xffd8) {
31083                     while (offset < maxOffset) {
31084                         markerBytes = dataView.getUint16(offset);
31085                         
31086                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31087                             markerLength = dataView.getUint16(offset + 2) + 2;
31088                             if (offset + markerLength > dataView.byteLength) {
31089                                 Roo.log('Invalid meta data: Invalid segment size.');
31090                                 break;
31091                             }
31092                             
31093                             if(markerBytes == 0xffe1){
31094                                 _this.parseExifData(
31095                                     dataView,
31096                                     offset,
31097                                     markerLength
31098                                 );
31099                             }
31100                             
31101                             offset += markerLength;
31102                             
31103                             continue;
31104                         }
31105                         
31106                         break;
31107                     }
31108                     
31109                 }
31110                 
31111                 var url = _this.urlAPI.createObjectURL(_this.file);
31112                 
31113                 _this.loadCanvas(url);
31114                 
31115                 return;
31116             }
31117             
31118             reader.readAsArrayBuffer(this.file);
31119             
31120         }
31121         
31122     },
31123     
31124     parseExifData : function(dataView, offset, length)
31125     {
31126         var tiffOffset = offset + 10,
31127             littleEndian,
31128             dirOffset;
31129     
31130         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31131             // No Exif data, might be XMP data instead
31132             return;
31133         }
31134         
31135         // Check for the ASCII code for "Exif" (0x45786966):
31136         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31137             // No Exif data, might be XMP data instead
31138             return;
31139         }
31140         if (tiffOffset + 8 > dataView.byteLength) {
31141             Roo.log('Invalid Exif data: Invalid segment size.');
31142             return;
31143         }
31144         // Check for the two null bytes:
31145         if (dataView.getUint16(offset + 8) !== 0x0000) {
31146             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31147             return;
31148         }
31149         // Check the byte alignment:
31150         switch (dataView.getUint16(tiffOffset)) {
31151         case 0x4949:
31152             littleEndian = true;
31153             break;
31154         case 0x4D4D:
31155             littleEndian = false;
31156             break;
31157         default:
31158             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31159             return;
31160         }
31161         // Check for the TIFF tag marker (0x002A):
31162         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31163             Roo.log('Invalid Exif data: Missing TIFF marker.');
31164             return;
31165         }
31166         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31167         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31168         
31169         this.parseExifTags(
31170             dataView,
31171             tiffOffset,
31172             tiffOffset + dirOffset,
31173             littleEndian
31174         );
31175     },
31176     
31177     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31178     {
31179         var tagsNumber,
31180             dirEndOffset,
31181             i;
31182         if (dirOffset + 6 > dataView.byteLength) {
31183             Roo.log('Invalid Exif data: Invalid directory offset.');
31184             return;
31185         }
31186         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31187         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31188         if (dirEndOffset + 4 > dataView.byteLength) {
31189             Roo.log('Invalid Exif data: Invalid directory size.');
31190             return;
31191         }
31192         for (i = 0; i < tagsNumber; i += 1) {
31193             this.parseExifTag(
31194                 dataView,
31195                 tiffOffset,
31196                 dirOffset + 2 + 12 * i, // tag offset
31197                 littleEndian
31198             );
31199         }
31200         // Return the offset to the next directory:
31201         return dataView.getUint32(dirEndOffset, littleEndian);
31202     },
31203     
31204     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31205     {
31206         var tag = dataView.getUint16(offset, littleEndian);
31207         
31208         this.exif[tag] = this.getExifValue(
31209             dataView,
31210             tiffOffset,
31211             offset,
31212             dataView.getUint16(offset + 2, littleEndian), // tag type
31213             dataView.getUint32(offset + 4, littleEndian), // tag length
31214             littleEndian
31215         );
31216     },
31217     
31218     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31219     {
31220         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31221             tagSize,
31222             dataOffset,
31223             values,
31224             i,
31225             str,
31226             c;
31227     
31228         if (!tagType) {
31229             Roo.log('Invalid Exif data: Invalid tag type.');
31230             return;
31231         }
31232         
31233         tagSize = tagType.size * length;
31234         // Determine if the value is contained in the dataOffset bytes,
31235         // or if the value at the dataOffset is a pointer to the actual data:
31236         dataOffset = tagSize > 4 ?
31237                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31238         if (dataOffset + tagSize > dataView.byteLength) {
31239             Roo.log('Invalid Exif data: Invalid data offset.');
31240             return;
31241         }
31242         if (length === 1) {
31243             return tagType.getValue(dataView, dataOffset, littleEndian);
31244         }
31245         values = [];
31246         for (i = 0; i < length; i += 1) {
31247             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31248         }
31249         
31250         if (tagType.ascii) {
31251             str = '';
31252             // Concatenate the chars:
31253             for (i = 0; i < values.length; i += 1) {
31254                 c = values[i];
31255                 // Ignore the terminating NULL byte(s):
31256                 if (c === '\u0000') {
31257                     break;
31258                 }
31259                 str += c;
31260             }
31261             return str;
31262         }
31263         return values;
31264     }
31265     
31266 });
31267
31268 Roo.apply(Roo.bootstrap.UploadCropbox, {
31269     tags : {
31270         'Orientation': 0x0112
31271     },
31272     
31273     Orientation: {
31274             1: 0, //'top-left',
31275 //            2: 'top-right',
31276             3: 180, //'bottom-right',
31277 //            4: 'bottom-left',
31278 //            5: 'left-top',
31279             6: 90, //'right-top',
31280 //            7: 'right-bottom',
31281             8: 270 //'left-bottom'
31282     },
31283     
31284     exifTagTypes : {
31285         // byte, 8-bit unsigned int:
31286         1: {
31287             getValue: function (dataView, dataOffset) {
31288                 return dataView.getUint8(dataOffset);
31289             },
31290             size: 1
31291         },
31292         // ascii, 8-bit byte:
31293         2: {
31294             getValue: function (dataView, dataOffset) {
31295                 return String.fromCharCode(dataView.getUint8(dataOffset));
31296             },
31297             size: 1,
31298             ascii: true
31299         },
31300         // short, 16 bit int:
31301         3: {
31302             getValue: function (dataView, dataOffset, littleEndian) {
31303                 return dataView.getUint16(dataOffset, littleEndian);
31304             },
31305             size: 2
31306         },
31307         // long, 32 bit int:
31308         4: {
31309             getValue: function (dataView, dataOffset, littleEndian) {
31310                 return dataView.getUint32(dataOffset, littleEndian);
31311             },
31312             size: 4
31313         },
31314         // rational = two long values, first is numerator, second is denominator:
31315         5: {
31316             getValue: function (dataView, dataOffset, littleEndian) {
31317                 return dataView.getUint32(dataOffset, littleEndian) /
31318                     dataView.getUint32(dataOffset + 4, littleEndian);
31319             },
31320             size: 8
31321         },
31322         // slong, 32 bit signed int:
31323         9: {
31324             getValue: function (dataView, dataOffset, littleEndian) {
31325                 return dataView.getInt32(dataOffset, littleEndian);
31326             },
31327             size: 4
31328         },
31329         // srational, two slongs, first is numerator, second is denominator:
31330         10: {
31331             getValue: function (dataView, dataOffset, littleEndian) {
31332                 return dataView.getInt32(dataOffset, littleEndian) /
31333                     dataView.getInt32(dataOffset + 4, littleEndian);
31334             },
31335             size: 8
31336         }
31337     },
31338     
31339     footer : {
31340         STANDARD : [
31341             {
31342                 tag : 'div',
31343                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31344                 action : 'rotate-left',
31345                 cn : [
31346                     {
31347                         tag : 'button',
31348                         cls : 'btn btn-default',
31349                         html : '<i class="fa fa-undo"></i>'
31350                     }
31351                 ]
31352             },
31353             {
31354                 tag : 'div',
31355                 cls : 'btn-group roo-upload-cropbox-picture',
31356                 action : 'picture',
31357                 cn : [
31358                     {
31359                         tag : 'button',
31360                         cls : 'btn btn-default',
31361                         html : '<i class="fa fa-picture-o"></i>'
31362                     }
31363                 ]
31364             },
31365             {
31366                 tag : 'div',
31367                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31368                 action : 'rotate-right',
31369                 cn : [
31370                     {
31371                         tag : 'button',
31372                         cls : 'btn btn-default',
31373                         html : '<i class="fa fa-repeat"></i>'
31374                     }
31375                 ]
31376             }
31377         ],
31378         DOCUMENT : [
31379             {
31380                 tag : 'div',
31381                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31382                 action : 'rotate-left',
31383                 cn : [
31384                     {
31385                         tag : 'button',
31386                         cls : 'btn btn-default',
31387                         html : '<i class="fa fa-undo"></i>'
31388                     }
31389                 ]
31390             },
31391             {
31392                 tag : 'div',
31393                 cls : 'btn-group roo-upload-cropbox-download',
31394                 action : 'download',
31395                 cn : [
31396                     {
31397                         tag : 'button',
31398                         cls : 'btn btn-default',
31399                         html : '<i class="fa fa-download"></i>'
31400                     }
31401                 ]
31402             },
31403             {
31404                 tag : 'div',
31405                 cls : 'btn-group roo-upload-cropbox-crop',
31406                 action : 'crop',
31407                 cn : [
31408                     {
31409                         tag : 'button',
31410                         cls : 'btn btn-default',
31411                         html : '<i class="fa fa-crop"></i>'
31412                     }
31413                 ]
31414             },
31415             {
31416                 tag : 'div',
31417                 cls : 'btn-group roo-upload-cropbox-trash',
31418                 action : 'trash',
31419                 cn : [
31420                     {
31421                         tag : 'button',
31422                         cls : 'btn btn-default',
31423                         html : '<i class="fa fa-trash"></i>'
31424                     }
31425                 ]
31426             },
31427             {
31428                 tag : 'div',
31429                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31430                 action : 'rotate-right',
31431                 cn : [
31432                     {
31433                         tag : 'button',
31434                         cls : 'btn btn-default',
31435                         html : '<i class="fa fa-repeat"></i>'
31436                     }
31437                 ]
31438             }
31439         ],
31440         ROTATOR : [
31441             {
31442                 tag : 'div',
31443                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31444                 action : 'rotate-left',
31445                 cn : [
31446                     {
31447                         tag : 'button',
31448                         cls : 'btn btn-default',
31449                         html : '<i class="fa fa-undo"></i>'
31450                     }
31451                 ]
31452             },
31453             {
31454                 tag : 'div',
31455                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31456                 action : 'rotate-right',
31457                 cn : [
31458                     {
31459                         tag : 'button',
31460                         cls : 'btn btn-default',
31461                         html : '<i class="fa fa-repeat"></i>'
31462                     }
31463                 ]
31464             }
31465         ]
31466     }
31467 });
31468
31469 /*
31470 * Licence: LGPL
31471 */
31472
31473 /**
31474  * @class Roo.bootstrap.DocumentManager
31475  * @extends Roo.bootstrap.Component
31476  * Bootstrap DocumentManager class
31477  * @cfg {String} paramName default 'imageUpload'
31478  * @cfg {String} toolTipName default 'filename'
31479  * @cfg {String} method default POST
31480  * @cfg {String} url action url
31481  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31482  * @cfg {Boolean} multiple multiple upload default true
31483  * @cfg {Number} thumbSize default 300
31484  * @cfg {String} fieldLabel
31485  * @cfg {Number} labelWidth default 4
31486  * @cfg {String} labelAlign (left|top) default left
31487  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31488 * @cfg {Number} labellg set the width of label (1-12)
31489  * @cfg {Number} labelmd set the width of label (1-12)
31490  * @cfg {Number} labelsm set the width of label (1-12)
31491  * @cfg {Number} labelxs set the width of label (1-12)
31492  * 
31493  * @constructor
31494  * Create a new DocumentManager
31495  * @param {Object} config The config object
31496  */
31497
31498 Roo.bootstrap.DocumentManager = function(config){
31499     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31500     
31501     this.files = [];
31502     this.delegates = [];
31503     
31504     this.addEvents({
31505         /**
31506          * @event initial
31507          * Fire when initial the DocumentManager
31508          * @param {Roo.bootstrap.DocumentManager} this
31509          */
31510         "initial" : true,
31511         /**
31512          * @event inspect
31513          * inspect selected file
31514          * @param {Roo.bootstrap.DocumentManager} this
31515          * @param {File} file
31516          */
31517         "inspect" : true,
31518         /**
31519          * @event exception
31520          * Fire when xhr load exception
31521          * @param {Roo.bootstrap.DocumentManager} this
31522          * @param {XMLHttpRequest} xhr
31523          */
31524         "exception" : true,
31525         /**
31526          * @event afterupload
31527          * Fire when xhr load exception
31528          * @param {Roo.bootstrap.DocumentManager} this
31529          * @param {XMLHttpRequest} xhr
31530          */
31531         "afterupload" : true,
31532         /**
31533          * @event prepare
31534          * prepare the form data
31535          * @param {Roo.bootstrap.DocumentManager} this
31536          * @param {Object} formData
31537          */
31538         "prepare" : true,
31539         /**
31540          * @event remove
31541          * Fire when remove the file
31542          * @param {Roo.bootstrap.DocumentManager} this
31543          * @param {Object} file
31544          */
31545         "remove" : true,
31546         /**
31547          * @event refresh
31548          * Fire after refresh the file
31549          * @param {Roo.bootstrap.DocumentManager} this
31550          */
31551         "refresh" : true,
31552         /**
31553          * @event click
31554          * Fire after click the image
31555          * @param {Roo.bootstrap.DocumentManager} this
31556          * @param {Object} file
31557          */
31558         "click" : true,
31559         /**
31560          * @event edit
31561          * Fire when upload a image and editable set to true
31562          * @param {Roo.bootstrap.DocumentManager} this
31563          * @param {Object} file
31564          */
31565         "edit" : true,
31566         /**
31567          * @event beforeselectfile
31568          * Fire before select file
31569          * @param {Roo.bootstrap.DocumentManager} this
31570          */
31571         "beforeselectfile" : true,
31572         /**
31573          * @event process
31574          * Fire before process file
31575          * @param {Roo.bootstrap.DocumentManager} this
31576          * @param {Object} file
31577          */
31578         "process" : true,
31579         /**
31580          * @event previewrendered
31581          * Fire when preview rendered
31582          * @param {Roo.bootstrap.DocumentManager} this
31583          * @param {Object} file
31584          */
31585         "previewrendered" : true,
31586         /**
31587          */
31588         "previewResize" : true
31589         
31590     });
31591 };
31592
31593 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31594     
31595     boxes : 0,
31596     inputName : '',
31597     thumbSize : 300,
31598     multiple : true,
31599     files : false,
31600     method : 'POST',
31601     url : '',
31602     paramName : 'imageUpload',
31603     toolTipName : 'filename',
31604     fieldLabel : '',
31605     labelWidth : 4,
31606     labelAlign : 'left',
31607     editable : true,
31608     delegates : false,
31609     xhr : false, 
31610     
31611     labellg : 0,
31612     labelmd : 0,
31613     labelsm : 0,
31614     labelxs : 0,
31615     
31616     getAutoCreate : function()
31617     {   
31618         var managerWidget = {
31619             tag : 'div',
31620             cls : 'roo-document-manager',
31621             cn : [
31622                 {
31623                     tag : 'input',
31624                     cls : 'roo-document-manager-selector',
31625                     type : 'file'
31626                 },
31627                 {
31628                     tag : 'div',
31629                     cls : 'roo-document-manager-uploader',
31630                     cn : [
31631                         {
31632                             tag : 'div',
31633                             cls : 'roo-document-manager-upload-btn',
31634                             html : '<i class="fa fa-plus"></i>'
31635                         }
31636                     ]
31637                     
31638                 }
31639             ]
31640         };
31641         
31642         var content = [
31643             {
31644                 tag : 'div',
31645                 cls : 'column col-md-12',
31646                 cn : managerWidget
31647             }
31648         ];
31649         
31650         if(this.fieldLabel.length){
31651             
31652             content = [
31653                 {
31654                     tag : 'div',
31655                     cls : 'column col-md-12',
31656                     html : this.fieldLabel
31657                 },
31658                 {
31659                     tag : 'div',
31660                     cls : 'column col-md-12',
31661                     cn : managerWidget
31662                 }
31663             ];
31664
31665             if(this.labelAlign == 'left'){
31666                 content = [
31667                     {
31668                         tag : 'div',
31669                         cls : 'column',
31670                         html : this.fieldLabel
31671                     },
31672                     {
31673                         tag : 'div',
31674                         cls : 'column',
31675                         cn : managerWidget
31676                     }
31677                 ];
31678                 
31679                 if(this.labelWidth > 12){
31680                     content[0].style = "width: " + this.labelWidth + 'px';
31681                 }
31682
31683                 if(this.labelWidth < 13 && this.labelmd == 0){
31684                     this.labelmd = this.labelWidth;
31685                 }
31686
31687                 if(this.labellg > 0){
31688                     content[0].cls += ' col-lg-' + this.labellg;
31689                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31690                 }
31691
31692                 if(this.labelmd > 0){
31693                     content[0].cls += ' col-md-' + this.labelmd;
31694                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31695                 }
31696
31697                 if(this.labelsm > 0){
31698                     content[0].cls += ' col-sm-' + this.labelsm;
31699                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31700                 }
31701
31702                 if(this.labelxs > 0){
31703                     content[0].cls += ' col-xs-' + this.labelxs;
31704                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31705                 }
31706                 
31707             }
31708         }
31709         
31710         var cfg = {
31711             tag : 'div',
31712             cls : 'row clearfix',
31713             cn : content
31714         };
31715         
31716         return cfg;
31717         
31718     },
31719     
31720     initEvents : function()
31721     {
31722         this.managerEl = this.el.select('.roo-document-manager', true).first();
31723         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31724         
31725         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31726         this.selectorEl.hide();
31727         
31728         if(this.multiple){
31729             this.selectorEl.attr('multiple', 'multiple');
31730         }
31731         
31732         this.selectorEl.on('change', this.onFileSelected, this);
31733         
31734         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31735         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31736         
31737         this.uploader.on('click', this.onUploaderClick, this);
31738         
31739         this.renderProgressDialog();
31740         
31741         var _this = this;
31742         
31743         window.addEventListener("resize", function() { _this.refresh(); } );
31744         
31745         this.fireEvent('initial', this);
31746     },
31747     
31748     renderProgressDialog : function()
31749     {
31750         var _this = this;
31751         
31752         this.progressDialog = new Roo.bootstrap.Modal({
31753             cls : 'roo-document-manager-progress-dialog',
31754             allow_close : false,
31755             animate : false,
31756             title : '',
31757             buttons : [
31758                 {
31759                     name  :'cancel',
31760                     weight : 'danger',
31761                     html : 'Cancel'
31762                 }
31763             ], 
31764             listeners : { 
31765                 btnclick : function() {
31766                     _this.uploadCancel();
31767                     this.hide();
31768                 }
31769             }
31770         });
31771          
31772         this.progressDialog.render(Roo.get(document.body));
31773          
31774         this.progress = new Roo.bootstrap.Progress({
31775             cls : 'roo-document-manager-progress',
31776             active : true,
31777             striped : true
31778         });
31779         
31780         this.progress.render(this.progressDialog.getChildContainer());
31781         
31782         this.progressBar = new Roo.bootstrap.ProgressBar({
31783             cls : 'roo-document-manager-progress-bar',
31784             aria_valuenow : 0,
31785             aria_valuemin : 0,
31786             aria_valuemax : 12,
31787             panel : 'success'
31788         });
31789         
31790         this.progressBar.render(this.progress.getChildContainer());
31791     },
31792     
31793     onUploaderClick : function(e)
31794     {
31795         e.preventDefault();
31796      
31797         if(this.fireEvent('beforeselectfile', this) != false){
31798             this.selectorEl.dom.click();
31799         }
31800         
31801     },
31802     
31803     onFileSelected : function(e)
31804     {
31805         e.preventDefault();
31806         
31807         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31808             return;
31809         }
31810         
31811         Roo.each(this.selectorEl.dom.files, function(file){
31812             if(this.fireEvent('inspect', this, file) != false){
31813                 this.files.push(file);
31814             }
31815         }, this);
31816         
31817         this.queue();
31818         
31819     },
31820     
31821     queue : function()
31822     {
31823         this.selectorEl.dom.value = '';
31824         
31825         if(!this.files || !this.files.length){
31826             return;
31827         }
31828         
31829         if(this.boxes > 0 && this.files.length > this.boxes){
31830             this.files = this.files.slice(0, this.boxes);
31831         }
31832         
31833         this.uploader.show();
31834         
31835         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31836             this.uploader.hide();
31837         }
31838         
31839         var _this = this;
31840         
31841         var files = [];
31842         
31843         var docs = [];
31844         
31845         Roo.each(this.files, function(file){
31846             
31847             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31848                 var f = this.renderPreview(file);
31849                 files.push(f);
31850                 return;
31851             }
31852             
31853             if(file.type.indexOf('image') != -1){
31854                 this.delegates.push(
31855                     (function(){
31856                         _this.process(file);
31857                     }).createDelegate(this)
31858                 );
31859         
31860                 return;
31861             }
31862             
31863             docs.push(
31864                 (function(){
31865                     _this.process(file);
31866                 }).createDelegate(this)
31867             );
31868             
31869         }, this);
31870         
31871         this.files = files;
31872         
31873         this.delegates = this.delegates.concat(docs);
31874         
31875         if(!this.delegates.length){
31876             this.refresh();
31877             return;
31878         }
31879         
31880         this.progressBar.aria_valuemax = this.delegates.length;
31881         
31882         this.arrange();
31883         
31884         return;
31885     },
31886     
31887     arrange : function()
31888     {
31889         if(!this.delegates.length){
31890             this.progressDialog.hide();
31891             this.refresh();
31892             return;
31893         }
31894         
31895         var delegate = this.delegates.shift();
31896         
31897         this.progressDialog.show();
31898         
31899         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31900         
31901         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31902         
31903         delegate();
31904     },
31905     
31906     refresh : function()
31907     {
31908         this.uploader.show();
31909         
31910         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31911             this.uploader.hide();
31912         }
31913         
31914         Roo.isTouch ? this.closable(false) : this.closable(true);
31915         
31916         this.fireEvent('refresh', this);
31917     },
31918     
31919     onRemove : function(e, el, o)
31920     {
31921         e.preventDefault();
31922         
31923         this.fireEvent('remove', this, o);
31924         
31925     },
31926     
31927     remove : function(o)
31928     {
31929         var files = [];
31930         
31931         Roo.each(this.files, function(file){
31932             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31933                 files.push(file);
31934                 return;
31935             }
31936
31937             o.target.remove();
31938
31939         }, this);
31940         
31941         this.files = files;
31942         
31943         this.refresh();
31944     },
31945     
31946     clear : function()
31947     {
31948         Roo.each(this.files, function(file){
31949             if(!file.target){
31950                 return;
31951             }
31952             
31953             file.target.remove();
31954
31955         }, this);
31956         
31957         this.files = [];
31958         
31959         this.refresh();
31960     },
31961     
31962     onClick : function(e, el, o)
31963     {
31964         e.preventDefault();
31965         
31966         this.fireEvent('click', this, o);
31967         
31968     },
31969     
31970     closable : function(closable)
31971     {
31972         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31973             
31974             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31975             
31976             if(closable){
31977                 el.show();
31978                 return;
31979             }
31980             
31981             el.hide();
31982             
31983         }, this);
31984     },
31985     
31986     xhrOnLoad : function(xhr)
31987     {
31988         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31989             el.remove();
31990         }, this);
31991         
31992         if (xhr.readyState !== 4) {
31993             this.arrange();
31994             this.fireEvent('exception', this, xhr);
31995             return;
31996         }
31997
31998         var response = Roo.decode(xhr.responseText);
31999         
32000         if(!response.success){
32001             this.arrange();
32002             this.fireEvent('exception', this, xhr);
32003             return;
32004         }
32005         
32006         var file = this.renderPreview(response.data);
32007         
32008         this.files.push(file);
32009         
32010         this.arrange();
32011         
32012         this.fireEvent('afterupload', this, xhr);
32013         
32014     },
32015     
32016     xhrOnError : function(xhr)
32017     {
32018         Roo.log('xhr on error');
32019         
32020         var response = Roo.decode(xhr.responseText);
32021           
32022         Roo.log(response);
32023         
32024         this.arrange();
32025     },
32026     
32027     process : function(file)
32028     {
32029         if(this.fireEvent('process', this, file) !== false){
32030             if(this.editable && file.type.indexOf('image') != -1){
32031                 this.fireEvent('edit', this, file);
32032                 return;
32033             }
32034
32035             this.uploadStart(file, false);
32036
32037             return;
32038         }
32039         
32040     },
32041     
32042     uploadStart : function(file, crop)
32043     {
32044         this.xhr = new XMLHttpRequest();
32045         
32046         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32047             this.arrange();
32048             return;
32049         }
32050         
32051         file.xhr = this.xhr;
32052             
32053         this.managerEl.createChild({
32054             tag : 'div',
32055             cls : 'roo-document-manager-loading',
32056             cn : [
32057                 {
32058                     tag : 'div',
32059                     tooltip : file.name,
32060                     cls : 'roo-document-manager-thumb',
32061                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32062                 }
32063             ]
32064
32065         });
32066
32067         this.xhr.open(this.method, this.url, true);
32068         
32069         var headers = {
32070             "Accept": "application/json",
32071             "Cache-Control": "no-cache",
32072             "X-Requested-With": "XMLHttpRequest"
32073         };
32074         
32075         for (var headerName in headers) {
32076             var headerValue = headers[headerName];
32077             if (headerValue) {
32078                 this.xhr.setRequestHeader(headerName, headerValue);
32079             }
32080         }
32081         
32082         var _this = this;
32083         
32084         this.xhr.onload = function()
32085         {
32086             _this.xhrOnLoad(_this.xhr);
32087         }
32088         
32089         this.xhr.onerror = function()
32090         {
32091             _this.xhrOnError(_this.xhr);
32092         }
32093         
32094         var formData = new FormData();
32095
32096         formData.append('returnHTML', 'NO');
32097         
32098         if(crop){
32099             formData.append('crop', crop);
32100         }
32101         
32102         formData.append(this.paramName, file, file.name);
32103         
32104         var options = {
32105             file : file, 
32106             manually : false
32107         };
32108         
32109         if(this.fireEvent('prepare', this, formData, options) != false){
32110             
32111             if(options.manually){
32112                 return;
32113             }
32114             
32115             this.xhr.send(formData);
32116             return;
32117         };
32118         
32119         this.uploadCancel();
32120     },
32121     
32122     uploadCancel : function()
32123     {
32124         if (this.xhr) {
32125             this.xhr.abort();
32126         }
32127         
32128         this.delegates = [];
32129         
32130         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32131             el.remove();
32132         }, this);
32133         
32134         this.arrange();
32135     },
32136     
32137     renderPreview : function(file)
32138     {
32139         if(typeof(file.target) != 'undefined' && file.target){
32140             return file;
32141         }
32142         
32143         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32144         
32145         var previewEl = this.managerEl.createChild({
32146             tag : 'div',
32147             cls : 'roo-document-manager-preview',
32148             cn : [
32149                 {
32150                     tag : 'div',
32151                     tooltip : file[this.toolTipName],
32152                     cls : 'roo-document-manager-thumb',
32153                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32154                 },
32155                 {
32156                     tag : 'button',
32157                     cls : 'close',
32158                     html : '<i class="fa fa-times-circle"></i>'
32159                 }
32160             ]
32161         });
32162
32163         var close = previewEl.select('button.close', true).first();
32164
32165         close.on('click', this.onRemove, this, file);
32166
32167         file.target = previewEl;
32168
32169         var image = previewEl.select('img', true).first();
32170         
32171         var _this = this;
32172         
32173         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32174         
32175         image.on('click', this.onClick, this, file);
32176         
32177         this.fireEvent('previewrendered', this, file);
32178         
32179         return file;
32180         
32181     },
32182     
32183     onPreviewLoad : function(file, image)
32184     {
32185         if(typeof(file.target) == 'undefined' || !file.target){
32186             return;
32187         }
32188         
32189         var width = image.dom.naturalWidth || image.dom.width;
32190         var height = image.dom.naturalHeight || image.dom.height;
32191         
32192         if(!this.previewResize) {
32193             return;
32194         }
32195         
32196         if(width > height){
32197             file.target.addClass('wide');
32198             return;
32199         }
32200         
32201         file.target.addClass('tall');
32202         return;
32203         
32204     },
32205     
32206     uploadFromSource : function(file, crop)
32207     {
32208         this.xhr = new XMLHttpRequest();
32209         
32210         this.managerEl.createChild({
32211             tag : 'div',
32212             cls : 'roo-document-manager-loading',
32213             cn : [
32214                 {
32215                     tag : 'div',
32216                     tooltip : file.name,
32217                     cls : 'roo-document-manager-thumb',
32218                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32219                 }
32220             ]
32221
32222         });
32223
32224         this.xhr.open(this.method, this.url, true);
32225         
32226         var headers = {
32227             "Accept": "application/json",
32228             "Cache-Control": "no-cache",
32229             "X-Requested-With": "XMLHttpRequest"
32230         };
32231         
32232         for (var headerName in headers) {
32233             var headerValue = headers[headerName];
32234             if (headerValue) {
32235                 this.xhr.setRequestHeader(headerName, headerValue);
32236             }
32237         }
32238         
32239         var _this = this;
32240         
32241         this.xhr.onload = function()
32242         {
32243             _this.xhrOnLoad(_this.xhr);
32244         }
32245         
32246         this.xhr.onerror = function()
32247         {
32248             _this.xhrOnError(_this.xhr);
32249         }
32250         
32251         var formData = new FormData();
32252
32253         formData.append('returnHTML', 'NO');
32254         
32255         formData.append('crop', crop);
32256         
32257         if(typeof(file.filename) != 'undefined'){
32258             formData.append('filename', file.filename);
32259         }
32260         
32261         if(typeof(file.mimetype) != 'undefined'){
32262             formData.append('mimetype', file.mimetype);
32263         }
32264         
32265         Roo.log(formData);
32266         
32267         if(this.fireEvent('prepare', this, formData) != false){
32268             this.xhr.send(formData);
32269         };
32270     }
32271 });
32272
32273 /*
32274 * Licence: LGPL
32275 */
32276
32277 /**
32278  * @class Roo.bootstrap.DocumentViewer
32279  * @extends Roo.bootstrap.Component
32280  * Bootstrap DocumentViewer class
32281  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32282  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32283  * 
32284  * @constructor
32285  * Create a new DocumentViewer
32286  * @param {Object} config The config object
32287  */
32288
32289 Roo.bootstrap.DocumentViewer = function(config){
32290     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32291     
32292     this.addEvents({
32293         /**
32294          * @event initial
32295          * Fire after initEvent
32296          * @param {Roo.bootstrap.DocumentViewer} this
32297          */
32298         "initial" : true,
32299         /**
32300          * @event click
32301          * Fire after click
32302          * @param {Roo.bootstrap.DocumentViewer} this
32303          */
32304         "click" : true,
32305         /**
32306          * @event download
32307          * Fire after download button
32308          * @param {Roo.bootstrap.DocumentViewer} this
32309          */
32310         "download" : true,
32311         /**
32312          * @event trash
32313          * Fire after trash button
32314          * @param {Roo.bootstrap.DocumentViewer} this
32315          */
32316         "trash" : true
32317         
32318     });
32319 };
32320
32321 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32322     
32323     showDownload : true,
32324     
32325     showTrash : true,
32326     
32327     getAutoCreate : function()
32328     {
32329         var cfg = {
32330             tag : 'div',
32331             cls : 'roo-document-viewer',
32332             cn : [
32333                 {
32334                     tag : 'div',
32335                     cls : 'roo-document-viewer-body',
32336                     cn : [
32337                         {
32338                             tag : 'div',
32339                             cls : 'roo-document-viewer-thumb',
32340                             cn : [
32341                                 {
32342                                     tag : 'img',
32343                                     cls : 'roo-document-viewer-image'
32344                                 }
32345                             ]
32346                         }
32347                     ]
32348                 },
32349                 {
32350                     tag : 'div',
32351                     cls : 'roo-document-viewer-footer',
32352                     cn : {
32353                         tag : 'div',
32354                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32355                         cn : [
32356                             {
32357                                 tag : 'div',
32358                                 cls : 'btn-group roo-document-viewer-download',
32359                                 cn : [
32360                                     {
32361                                         tag : 'button',
32362                                         cls : 'btn btn-default',
32363                                         html : '<i class="fa fa-download"></i>'
32364                                     }
32365                                 ]
32366                             },
32367                             {
32368                                 tag : 'div',
32369                                 cls : 'btn-group roo-document-viewer-trash',
32370                                 cn : [
32371                                     {
32372                                         tag : 'button',
32373                                         cls : 'btn btn-default',
32374                                         html : '<i class="fa fa-trash"></i>'
32375                                     }
32376                                 ]
32377                             }
32378                         ]
32379                     }
32380                 }
32381             ]
32382         };
32383         
32384         return cfg;
32385     },
32386     
32387     initEvents : function()
32388     {
32389         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32390         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32391         
32392         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32393         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32394         
32395         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32396         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32397         
32398         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32399         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32400         
32401         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32402         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32403         
32404         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32405         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32406         
32407         this.bodyEl.on('click', this.onClick, this);
32408         this.downloadBtn.on('click', this.onDownload, this);
32409         this.trashBtn.on('click', this.onTrash, this);
32410         
32411         this.downloadBtn.hide();
32412         this.trashBtn.hide();
32413         
32414         if(this.showDownload){
32415             this.downloadBtn.show();
32416         }
32417         
32418         if(this.showTrash){
32419             this.trashBtn.show();
32420         }
32421         
32422         if(!this.showDownload && !this.showTrash) {
32423             this.footerEl.hide();
32424         }
32425         
32426     },
32427     
32428     initial : function()
32429     {
32430         this.fireEvent('initial', this);
32431         
32432     },
32433     
32434     onClick : function(e)
32435     {
32436         e.preventDefault();
32437         
32438         this.fireEvent('click', this);
32439     },
32440     
32441     onDownload : function(e)
32442     {
32443         e.preventDefault();
32444         
32445         this.fireEvent('download', this);
32446     },
32447     
32448     onTrash : function(e)
32449     {
32450         e.preventDefault();
32451         
32452         this.fireEvent('trash', this);
32453     }
32454     
32455 });
32456 /*
32457  * - LGPL
32458  *
32459  * nav progress bar
32460  * 
32461  */
32462
32463 /**
32464  * @class Roo.bootstrap.NavProgressBar
32465  * @extends Roo.bootstrap.Component
32466  * Bootstrap NavProgressBar class
32467  * 
32468  * @constructor
32469  * Create a new nav progress bar
32470  * @param {Object} config The config object
32471  */
32472
32473 Roo.bootstrap.NavProgressBar = function(config){
32474     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32475
32476     this.bullets = this.bullets || [];
32477    
32478 //    Roo.bootstrap.NavProgressBar.register(this);
32479      this.addEvents({
32480         /**
32481              * @event changed
32482              * Fires when the active item changes
32483              * @param {Roo.bootstrap.NavProgressBar} this
32484              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32485              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32486          */
32487         'changed': true
32488      });
32489     
32490 };
32491
32492 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32493     
32494     bullets : [],
32495     barItems : [],
32496     
32497     getAutoCreate : function()
32498     {
32499         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32500         
32501         cfg = {
32502             tag : 'div',
32503             cls : 'roo-navigation-bar-group',
32504             cn : [
32505                 {
32506                     tag : 'div',
32507                     cls : 'roo-navigation-top-bar'
32508                 },
32509                 {
32510                     tag : 'div',
32511                     cls : 'roo-navigation-bullets-bar',
32512                     cn : [
32513                         {
32514                             tag : 'ul',
32515                             cls : 'roo-navigation-bar'
32516                         }
32517                     ]
32518                 },
32519                 
32520                 {
32521                     tag : 'div',
32522                     cls : 'roo-navigation-bottom-bar'
32523                 }
32524             ]
32525             
32526         };
32527         
32528         return cfg;
32529         
32530     },
32531     
32532     initEvents: function() 
32533     {
32534         
32535     },
32536     
32537     onRender : function(ct, position) 
32538     {
32539         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32540         
32541         if(this.bullets.length){
32542             Roo.each(this.bullets, function(b){
32543                this.addItem(b);
32544             }, this);
32545         }
32546         
32547         this.format();
32548         
32549     },
32550     
32551     addItem : function(cfg)
32552     {
32553         var item = new Roo.bootstrap.NavProgressItem(cfg);
32554         
32555         item.parentId = this.id;
32556         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32557         
32558         if(cfg.html){
32559             var top = new Roo.bootstrap.Element({
32560                 tag : 'div',
32561                 cls : 'roo-navigation-bar-text'
32562             });
32563             
32564             var bottom = new Roo.bootstrap.Element({
32565                 tag : 'div',
32566                 cls : 'roo-navigation-bar-text'
32567             });
32568             
32569             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32570             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32571             
32572             var topText = new Roo.bootstrap.Element({
32573                 tag : 'span',
32574                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32575             });
32576             
32577             var bottomText = new Roo.bootstrap.Element({
32578                 tag : 'span',
32579                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32580             });
32581             
32582             topText.onRender(top.el, null);
32583             bottomText.onRender(bottom.el, null);
32584             
32585             item.topEl = top;
32586             item.bottomEl = bottom;
32587         }
32588         
32589         this.barItems.push(item);
32590         
32591         return item;
32592     },
32593     
32594     getActive : function()
32595     {
32596         var active = false;
32597         
32598         Roo.each(this.barItems, function(v){
32599             
32600             if (!v.isActive()) {
32601                 return;
32602             }
32603             
32604             active = v;
32605             return false;
32606             
32607         });
32608         
32609         return active;
32610     },
32611     
32612     setActiveItem : function(item)
32613     {
32614         var prev = false;
32615         
32616         Roo.each(this.barItems, function(v){
32617             if (v.rid == item.rid) {
32618                 return ;
32619             }
32620             
32621             if (v.isActive()) {
32622                 v.setActive(false);
32623                 prev = v;
32624             }
32625         });
32626
32627         item.setActive(true);
32628         
32629         this.fireEvent('changed', this, item, prev);
32630     },
32631     
32632     getBarItem: function(rid)
32633     {
32634         var ret = false;
32635         
32636         Roo.each(this.barItems, function(e) {
32637             if (e.rid != rid) {
32638                 return;
32639             }
32640             
32641             ret =  e;
32642             return false;
32643         });
32644         
32645         return ret;
32646     },
32647     
32648     indexOfItem : function(item)
32649     {
32650         var index = false;
32651         
32652         Roo.each(this.barItems, function(v, i){
32653             
32654             if (v.rid != item.rid) {
32655                 return;
32656             }
32657             
32658             index = i;
32659             return false
32660         });
32661         
32662         return index;
32663     },
32664     
32665     setActiveNext : function()
32666     {
32667         var i = this.indexOfItem(this.getActive());
32668         
32669         if (i > this.barItems.length) {
32670             return;
32671         }
32672         
32673         this.setActiveItem(this.barItems[i+1]);
32674     },
32675     
32676     setActivePrev : function()
32677     {
32678         var i = this.indexOfItem(this.getActive());
32679         
32680         if (i  < 1) {
32681             return;
32682         }
32683         
32684         this.setActiveItem(this.barItems[i-1]);
32685     },
32686     
32687     format : function()
32688     {
32689         if(!this.barItems.length){
32690             return;
32691         }
32692      
32693         var width = 100 / this.barItems.length;
32694         
32695         Roo.each(this.barItems, function(i){
32696             i.el.setStyle('width', width + '%');
32697             i.topEl.el.setStyle('width', width + '%');
32698             i.bottomEl.el.setStyle('width', width + '%');
32699         }, this);
32700         
32701     }
32702     
32703 });
32704 /*
32705  * - LGPL
32706  *
32707  * Nav Progress Item
32708  * 
32709  */
32710
32711 /**
32712  * @class Roo.bootstrap.NavProgressItem
32713  * @extends Roo.bootstrap.Component
32714  * Bootstrap NavProgressItem class
32715  * @cfg {String} rid the reference id
32716  * @cfg {Boolean} active (true|false) Is item active default false
32717  * @cfg {Boolean} disabled (true|false) Is item active default false
32718  * @cfg {String} html
32719  * @cfg {String} position (top|bottom) text position default bottom
32720  * @cfg {String} icon show icon instead of number
32721  * 
32722  * @constructor
32723  * Create a new NavProgressItem
32724  * @param {Object} config The config object
32725  */
32726 Roo.bootstrap.NavProgressItem = function(config){
32727     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32728     this.addEvents({
32729         // raw events
32730         /**
32731          * @event click
32732          * The raw click event for the entire grid.
32733          * @param {Roo.bootstrap.NavProgressItem} this
32734          * @param {Roo.EventObject} e
32735          */
32736         "click" : true
32737     });
32738    
32739 };
32740
32741 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32742     
32743     rid : '',
32744     active : false,
32745     disabled : false,
32746     html : '',
32747     position : 'bottom',
32748     icon : false,
32749     
32750     getAutoCreate : function()
32751     {
32752         var iconCls = 'roo-navigation-bar-item-icon';
32753         
32754         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32755         
32756         var cfg = {
32757             tag: 'li',
32758             cls: 'roo-navigation-bar-item',
32759             cn : [
32760                 {
32761                     tag : 'i',
32762                     cls : iconCls
32763                 }
32764             ]
32765         };
32766         
32767         if(this.active){
32768             cfg.cls += ' active';
32769         }
32770         if(this.disabled){
32771             cfg.cls += ' disabled';
32772         }
32773         
32774         return cfg;
32775     },
32776     
32777     disable : function()
32778     {
32779         this.setDisabled(true);
32780     },
32781     
32782     enable : function()
32783     {
32784         this.setDisabled(false);
32785     },
32786     
32787     initEvents: function() 
32788     {
32789         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32790         
32791         this.iconEl.on('click', this.onClick, this);
32792     },
32793     
32794     onClick : function(e)
32795     {
32796         e.preventDefault();
32797         
32798         if(this.disabled){
32799             return;
32800         }
32801         
32802         if(this.fireEvent('click', this, e) === false){
32803             return;
32804         };
32805         
32806         this.parent().setActiveItem(this);
32807     },
32808     
32809     isActive: function () 
32810     {
32811         return this.active;
32812     },
32813     
32814     setActive : function(state)
32815     {
32816         if(this.active == state){
32817             return;
32818         }
32819         
32820         this.active = state;
32821         
32822         if (state) {
32823             this.el.addClass('active');
32824             return;
32825         }
32826         
32827         this.el.removeClass('active');
32828         
32829         return;
32830     },
32831     
32832     setDisabled : function(state)
32833     {
32834         if(this.disabled == state){
32835             return;
32836         }
32837         
32838         this.disabled = state;
32839         
32840         if (state) {
32841             this.el.addClass('disabled');
32842             return;
32843         }
32844         
32845         this.el.removeClass('disabled');
32846     },
32847     
32848     tooltipEl : function()
32849     {
32850         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32851     }
32852 });
32853  
32854
32855  /*
32856  * - LGPL
32857  *
32858  * FieldLabel
32859  * 
32860  */
32861
32862 /**
32863  * @class Roo.bootstrap.FieldLabel
32864  * @extends Roo.bootstrap.Component
32865  * Bootstrap FieldLabel class
32866  * @cfg {String} html contents of the element
32867  * @cfg {String} tag tag of the element default label
32868  * @cfg {String} cls class of the element
32869  * @cfg {String} target label target 
32870  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32871  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32872  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32873  * @cfg {String} iconTooltip default "This field is required"
32874  * @cfg {String} indicatorpos (left|right) default left
32875  * 
32876  * @constructor
32877  * Create a new FieldLabel
32878  * @param {Object} config The config object
32879  */
32880
32881 Roo.bootstrap.FieldLabel = function(config){
32882     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32883     
32884     this.addEvents({
32885             /**
32886              * @event invalid
32887              * Fires after the field has been marked as invalid.
32888              * @param {Roo.form.FieldLabel} this
32889              * @param {String} msg The validation message
32890              */
32891             invalid : true,
32892             /**
32893              * @event valid
32894              * Fires after the field has been validated with no errors.
32895              * @param {Roo.form.FieldLabel} this
32896              */
32897             valid : true
32898         });
32899 };
32900
32901 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32902     
32903     tag: 'label',
32904     cls: '',
32905     html: '',
32906     target: '',
32907     allowBlank : true,
32908     invalidClass : 'has-warning',
32909     validClass : 'has-success',
32910     iconTooltip : 'This field is required',
32911     indicatorpos : 'left',
32912     
32913     getAutoCreate : function(){
32914         
32915         var cls = "";
32916         if (!this.allowBlank) {
32917             cls  = "visible";
32918         }
32919         
32920         var cfg = {
32921             tag : this.tag,
32922             cls : 'roo-bootstrap-field-label ' + this.cls,
32923             for : this.target,
32924             cn : [
32925                 {
32926                     tag : 'i',
32927                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32928                     tooltip : this.iconTooltip
32929                 },
32930                 {
32931                     tag : 'span',
32932                     html : this.html
32933                 }
32934             ] 
32935         };
32936         
32937         if(this.indicatorpos == 'right'){
32938             var cfg = {
32939                 tag : this.tag,
32940                 cls : 'roo-bootstrap-field-label ' + this.cls,
32941                 for : this.target,
32942                 cn : [
32943                     {
32944                         tag : 'span',
32945                         html : this.html
32946                     },
32947                     {
32948                         tag : 'i',
32949                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32950                         tooltip : this.iconTooltip
32951                     }
32952                 ] 
32953             };
32954         }
32955         
32956         return cfg;
32957     },
32958     
32959     initEvents: function() 
32960     {
32961         Roo.bootstrap.Element.superclass.initEvents.call(this);
32962         
32963         this.indicator = this.indicatorEl();
32964         
32965         if(this.indicator){
32966             this.indicator.removeClass('visible');
32967             this.indicator.addClass('invisible');
32968         }
32969         
32970         Roo.bootstrap.FieldLabel.register(this);
32971     },
32972     
32973     indicatorEl : function()
32974     {
32975         var indicator = this.el.select('i.roo-required-indicator',true).first();
32976         
32977         if(!indicator){
32978             return false;
32979         }
32980         
32981         return indicator;
32982         
32983     },
32984     
32985     /**
32986      * Mark this field as valid
32987      */
32988     markValid : function()
32989     {
32990         if(this.indicator){
32991             this.indicator.removeClass('visible');
32992             this.indicator.addClass('invisible');
32993         }
32994         if (Roo.bootstrap.version == 3) {
32995             this.el.removeClass(this.invalidClass);
32996             this.el.addClass(this.validClass);
32997         } else {
32998             this.el.removeClass('is-invalid');
32999             this.el.addClass('is-valid');
33000         }
33001         
33002         
33003         this.fireEvent('valid', this);
33004     },
33005     
33006     /**
33007      * Mark this field as invalid
33008      * @param {String} msg The validation message
33009      */
33010     markInvalid : function(msg)
33011     {
33012         if(this.indicator){
33013             this.indicator.removeClass('invisible');
33014             this.indicator.addClass('visible');
33015         }
33016           if (Roo.bootstrap.version == 3) {
33017             this.el.removeClass(this.validClass);
33018             this.el.addClass(this.invalidClass);
33019         } else {
33020             this.el.removeClass('is-valid');
33021             this.el.addClass('is-invalid');
33022         }
33023         
33024         
33025         this.fireEvent('invalid', this, msg);
33026     }
33027     
33028    
33029 });
33030
33031 Roo.apply(Roo.bootstrap.FieldLabel, {
33032     
33033     groups: {},
33034     
33035      /**
33036     * register a FieldLabel Group
33037     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33038     */
33039     register : function(label)
33040     {
33041         if(this.groups.hasOwnProperty(label.target)){
33042             return;
33043         }
33044      
33045         this.groups[label.target] = label;
33046         
33047     },
33048     /**
33049     * fetch a FieldLabel Group based on the target
33050     * @param {string} target
33051     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33052     */
33053     get: function(target) {
33054         if (typeof(this.groups[target]) == 'undefined') {
33055             return false;
33056         }
33057         
33058         return this.groups[target] ;
33059     }
33060 });
33061
33062  
33063
33064  /*
33065  * - LGPL
33066  *
33067  * page DateSplitField.
33068  * 
33069  */
33070
33071
33072 /**
33073  * @class Roo.bootstrap.DateSplitField
33074  * @extends Roo.bootstrap.Component
33075  * Bootstrap DateSplitField class
33076  * @cfg {string} fieldLabel - the label associated
33077  * @cfg {Number} labelWidth set the width of label (0-12)
33078  * @cfg {String} labelAlign (top|left)
33079  * @cfg {Boolean} dayAllowBlank (true|false) default false
33080  * @cfg {Boolean} monthAllowBlank (true|false) default false
33081  * @cfg {Boolean} yearAllowBlank (true|false) default false
33082  * @cfg {string} dayPlaceholder 
33083  * @cfg {string} monthPlaceholder
33084  * @cfg {string} yearPlaceholder
33085  * @cfg {string} dayFormat default 'd'
33086  * @cfg {string} monthFormat default 'm'
33087  * @cfg {string} yearFormat default 'Y'
33088  * @cfg {Number} labellg set the width of label (1-12)
33089  * @cfg {Number} labelmd set the width of label (1-12)
33090  * @cfg {Number} labelsm set the width of label (1-12)
33091  * @cfg {Number} labelxs set the width of label (1-12)
33092
33093  *     
33094  * @constructor
33095  * Create a new DateSplitField
33096  * @param {Object} config The config object
33097  */
33098
33099 Roo.bootstrap.DateSplitField = function(config){
33100     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33101     
33102     this.addEvents({
33103         // raw events
33104          /**
33105          * @event years
33106          * getting the data of years
33107          * @param {Roo.bootstrap.DateSplitField} this
33108          * @param {Object} years
33109          */
33110         "years" : true,
33111         /**
33112          * @event days
33113          * getting the data of days
33114          * @param {Roo.bootstrap.DateSplitField} this
33115          * @param {Object} days
33116          */
33117         "days" : true,
33118         /**
33119          * @event invalid
33120          * Fires after the field has been marked as invalid.
33121          * @param {Roo.form.Field} this
33122          * @param {String} msg The validation message
33123          */
33124         invalid : true,
33125        /**
33126          * @event valid
33127          * Fires after the field has been validated with no errors.
33128          * @param {Roo.form.Field} this
33129          */
33130         valid : true
33131     });
33132 };
33133
33134 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33135     
33136     fieldLabel : '',
33137     labelAlign : 'top',
33138     labelWidth : 3,
33139     dayAllowBlank : false,
33140     monthAllowBlank : false,
33141     yearAllowBlank : false,
33142     dayPlaceholder : '',
33143     monthPlaceholder : '',
33144     yearPlaceholder : '',
33145     dayFormat : 'd',
33146     monthFormat : 'm',
33147     yearFormat : 'Y',
33148     isFormField : true,
33149     labellg : 0,
33150     labelmd : 0,
33151     labelsm : 0,
33152     labelxs : 0,
33153     
33154     getAutoCreate : function()
33155     {
33156         var cfg = {
33157             tag : 'div',
33158             cls : 'row roo-date-split-field-group',
33159             cn : [
33160                 {
33161                     tag : 'input',
33162                     type : 'hidden',
33163                     cls : 'form-hidden-field roo-date-split-field-group-value',
33164                     name : this.name
33165                 }
33166             ]
33167         };
33168         
33169         var labelCls = 'col-md-12';
33170         var contentCls = 'col-md-4';
33171         
33172         if(this.fieldLabel){
33173             
33174             var label = {
33175                 tag : 'div',
33176                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33177                 cn : [
33178                     {
33179                         tag : 'label',
33180                         html : this.fieldLabel
33181                     }
33182                 ]
33183             };
33184             
33185             if(this.labelAlign == 'left'){
33186             
33187                 if(this.labelWidth > 12){
33188                     label.style = "width: " + this.labelWidth + 'px';
33189                 }
33190
33191                 if(this.labelWidth < 13 && this.labelmd == 0){
33192                     this.labelmd = this.labelWidth;
33193                 }
33194
33195                 if(this.labellg > 0){
33196                     labelCls = ' col-lg-' + this.labellg;
33197                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33198                 }
33199
33200                 if(this.labelmd > 0){
33201                     labelCls = ' col-md-' + this.labelmd;
33202                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33203                 }
33204
33205                 if(this.labelsm > 0){
33206                     labelCls = ' col-sm-' + this.labelsm;
33207                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33208                 }
33209
33210                 if(this.labelxs > 0){
33211                     labelCls = ' col-xs-' + this.labelxs;
33212                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33213                 }
33214             }
33215             
33216             label.cls += ' ' + labelCls;
33217             
33218             cfg.cn.push(label);
33219         }
33220         
33221         Roo.each(['day', 'month', 'year'], function(t){
33222             cfg.cn.push({
33223                 tag : 'div',
33224                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33225             });
33226         }, this);
33227         
33228         return cfg;
33229     },
33230     
33231     inputEl: function ()
33232     {
33233         return this.el.select('.roo-date-split-field-group-value', true).first();
33234     },
33235     
33236     onRender : function(ct, position) 
33237     {
33238         var _this = this;
33239         
33240         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33241         
33242         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33243         
33244         this.dayField = new Roo.bootstrap.ComboBox({
33245             allowBlank : this.dayAllowBlank,
33246             alwaysQuery : true,
33247             displayField : 'value',
33248             editable : false,
33249             fieldLabel : '',
33250             forceSelection : true,
33251             mode : 'local',
33252             placeholder : this.dayPlaceholder,
33253             selectOnFocus : true,
33254             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33255             triggerAction : 'all',
33256             typeAhead : true,
33257             valueField : 'value',
33258             store : new Roo.data.SimpleStore({
33259                 data : (function() {    
33260                     var days = [];
33261                     _this.fireEvent('days', _this, days);
33262                     return days;
33263                 })(),
33264                 fields : [ 'value' ]
33265             }),
33266             listeners : {
33267                 select : function (_self, record, index)
33268                 {
33269                     _this.setValue(_this.getValue());
33270                 }
33271             }
33272         });
33273
33274         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33275         
33276         this.monthField = new Roo.bootstrap.MonthField({
33277             after : '<i class=\"fa fa-calendar\"></i>',
33278             allowBlank : this.monthAllowBlank,
33279             placeholder : this.monthPlaceholder,
33280             readOnly : true,
33281             listeners : {
33282                 render : function (_self)
33283                 {
33284                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33285                         e.preventDefault();
33286                         _self.focus();
33287                     });
33288                 },
33289                 select : function (_self, oldvalue, newvalue)
33290                 {
33291                     _this.setValue(_this.getValue());
33292                 }
33293             }
33294         });
33295         
33296         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33297         
33298         this.yearField = new Roo.bootstrap.ComboBox({
33299             allowBlank : this.yearAllowBlank,
33300             alwaysQuery : true,
33301             displayField : 'value',
33302             editable : false,
33303             fieldLabel : '',
33304             forceSelection : true,
33305             mode : 'local',
33306             placeholder : this.yearPlaceholder,
33307             selectOnFocus : true,
33308             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33309             triggerAction : 'all',
33310             typeAhead : true,
33311             valueField : 'value',
33312             store : new Roo.data.SimpleStore({
33313                 data : (function() {
33314                     var years = [];
33315                     _this.fireEvent('years', _this, years);
33316                     return years;
33317                 })(),
33318                 fields : [ 'value' ]
33319             }),
33320             listeners : {
33321                 select : function (_self, record, index)
33322                 {
33323                     _this.setValue(_this.getValue());
33324                 }
33325             }
33326         });
33327
33328         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33329     },
33330     
33331     setValue : function(v, format)
33332     {
33333         this.inputEl.dom.value = v;
33334         
33335         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33336         
33337         var d = Date.parseDate(v, f);
33338         
33339         if(!d){
33340             this.validate();
33341             return;
33342         }
33343         
33344         this.setDay(d.format(this.dayFormat));
33345         this.setMonth(d.format(this.monthFormat));
33346         this.setYear(d.format(this.yearFormat));
33347         
33348         this.validate();
33349         
33350         return;
33351     },
33352     
33353     setDay : function(v)
33354     {
33355         this.dayField.setValue(v);
33356         this.inputEl.dom.value = this.getValue();
33357         this.validate();
33358         return;
33359     },
33360     
33361     setMonth : function(v)
33362     {
33363         this.monthField.setValue(v, true);
33364         this.inputEl.dom.value = this.getValue();
33365         this.validate();
33366         return;
33367     },
33368     
33369     setYear : function(v)
33370     {
33371         this.yearField.setValue(v);
33372         this.inputEl.dom.value = this.getValue();
33373         this.validate();
33374         return;
33375     },
33376     
33377     getDay : function()
33378     {
33379         return this.dayField.getValue();
33380     },
33381     
33382     getMonth : function()
33383     {
33384         return this.monthField.getValue();
33385     },
33386     
33387     getYear : function()
33388     {
33389         return this.yearField.getValue();
33390     },
33391     
33392     getValue : function()
33393     {
33394         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33395         
33396         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33397         
33398         return date;
33399     },
33400     
33401     reset : function()
33402     {
33403         this.setDay('');
33404         this.setMonth('');
33405         this.setYear('');
33406         this.inputEl.dom.value = '';
33407         this.validate();
33408         return;
33409     },
33410     
33411     validate : function()
33412     {
33413         var d = this.dayField.validate();
33414         var m = this.monthField.validate();
33415         var y = this.yearField.validate();
33416         
33417         var valid = true;
33418         
33419         if(
33420                 (!this.dayAllowBlank && !d) ||
33421                 (!this.monthAllowBlank && !m) ||
33422                 (!this.yearAllowBlank && !y)
33423         ){
33424             valid = false;
33425         }
33426         
33427         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33428             return valid;
33429         }
33430         
33431         if(valid){
33432             this.markValid();
33433             return valid;
33434         }
33435         
33436         this.markInvalid();
33437         
33438         return valid;
33439     },
33440     
33441     markValid : function()
33442     {
33443         
33444         var label = this.el.select('label', true).first();
33445         var icon = this.el.select('i.fa-star', true).first();
33446
33447         if(label && icon){
33448             icon.remove();
33449         }
33450         
33451         this.fireEvent('valid', this);
33452     },
33453     
33454      /**
33455      * Mark this field as invalid
33456      * @param {String} msg The validation message
33457      */
33458     markInvalid : function(msg)
33459     {
33460         
33461         var label = this.el.select('label', true).first();
33462         var icon = this.el.select('i.fa-star', true).first();
33463
33464         if(label && !icon){
33465             this.el.select('.roo-date-split-field-label', true).createChild({
33466                 tag : 'i',
33467                 cls : 'text-danger fa fa-lg fa-star',
33468                 tooltip : 'This field is required',
33469                 style : 'margin-right:5px;'
33470             }, label, true);
33471         }
33472         
33473         this.fireEvent('invalid', this, msg);
33474     },
33475     
33476     clearInvalid : function()
33477     {
33478         var label = this.el.select('label', true).first();
33479         var icon = this.el.select('i.fa-star', true).first();
33480
33481         if(label && icon){
33482             icon.remove();
33483         }
33484         
33485         this.fireEvent('valid', this);
33486     },
33487     
33488     getName: function()
33489     {
33490         return this.name;
33491     }
33492     
33493 });
33494
33495  /**
33496  *
33497  * This is based on 
33498  * http://masonry.desandro.com
33499  *
33500  * The idea is to render all the bricks based on vertical width...
33501  *
33502  * The original code extends 'outlayer' - we might need to use that....
33503  * 
33504  */
33505
33506
33507 /**
33508  * @class Roo.bootstrap.LayoutMasonry
33509  * @extends Roo.bootstrap.Component
33510  * Bootstrap Layout Masonry class
33511  * 
33512  * @constructor
33513  * Create a new Element
33514  * @param {Object} config The config object
33515  */
33516
33517 Roo.bootstrap.LayoutMasonry = function(config){
33518     
33519     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33520     
33521     this.bricks = [];
33522     
33523     Roo.bootstrap.LayoutMasonry.register(this);
33524     
33525     this.addEvents({
33526         // raw events
33527         /**
33528          * @event layout
33529          * Fire after layout the items
33530          * @param {Roo.bootstrap.LayoutMasonry} this
33531          * @param {Roo.EventObject} e
33532          */
33533         "layout" : true
33534     });
33535     
33536 };
33537
33538 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33539     
33540     /**
33541      * @cfg {Boolean} isLayoutInstant = no animation?
33542      */   
33543     isLayoutInstant : false, // needed?
33544    
33545     /**
33546      * @cfg {Number} boxWidth  width of the columns
33547      */   
33548     boxWidth : 450,
33549     
33550       /**
33551      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33552      */   
33553     boxHeight : 0,
33554     
33555     /**
33556      * @cfg {Number} padWidth padding below box..
33557      */   
33558     padWidth : 10, 
33559     
33560     /**
33561      * @cfg {Number} gutter gutter width..
33562      */   
33563     gutter : 10,
33564     
33565      /**
33566      * @cfg {Number} maxCols maximum number of columns
33567      */   
33568     
33569     maxCols: 0,
33570     
33571     /**
33572      * @cfg {Boolean} isAutoInitial defalut true
33573      */   
33574     isAutoInitial : true, 
33575     
33576     containerWidth: 0,
33577     
33578     /**
33579      * @cfg {Boolean} isHorizontal defalut false
33580      */   
33581     isHorizontal : false, 
33582
33583     currentSize : null,
33584     
33585     tag: 'div',
33586     
33587     cls: '',
33588     
33589     bricks: null, //CompositeElement
33590     
33591     cols : 1,
33592     
33593     _isLayoutInited : false,
33594     
33595 //    isAlternative : false, // only use for vertical layout...
33596     
33597     /**
33598      * @cfg {Number} alternativePadWidth padding below box..
33599      */   
33600     alternativePadWidth : 50,
33601     
33602     selectedBrick : [],
33603     
33604     getAutoCreate : function(){
33605         
33606         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33607         
33608         var cfg = {
33609             tag: this.tag,
33610             cls: 'blog-masonary-wrapper ' + this.cls,
33611             cn : {
33612                 cls : 'mas-boxes masonary'
33613             }
33614         };
33615         
33616         return cfg;
33617     },
33618     
33619     getChildContainer: function( )
33620     {
33621         if (this.boxesEl) {
33622             return this.boxesEl;
33623         }
33624         
33625         this.boxesEl = this.el.select('.mas-boxes').first();
33626         
33627         return this.boxesEl;
33628     },
33629     
33630     
33631     initEvents : function()
33632     {
33633         var _this = this;
33634         
33635         if(this.isAutoInitial){
33636             Roo.log('hook children rendered');
33637             this.on('childrenrendered', function() {
33638                 Roo.log('children rendered');
33639                 _this.initial();
33640             } ,this);
33641         }
33642     },
33643     
33644     initial : function()
33645     {
33646         this.selectedBrick = [];
33647         
33648         this.currentSize = this.el.getBox(true);
33649         
33650         Roo.EventManager.onWindowResize(this.resize, this); 
33651
33652         if(!this.isAutoInitial){
33653             this.layout();
33654             return;
33655         }
33656         
33657         this.layout();
33658         
33659         return;
33660         //this.layout.defer(500,this);
33661         
33662     },
33663     
33664     resize : function()
33665     {
33666         var cs = this.el.getBox(true);
33667         
33668         if (
33669                 this.currentSize.width == cs.width && 
33670                 this.currentSize.x == cs.x && 
33671                 this.currentSize.height == cs.height && 
33672                 this.currentSize.y == cs.y 
33673         ) {
33674             Roo.log("no change in with or X or Y");
33675             return;
33676         }
33677         
33678         this.currentSize = cs;
33679         
33680         this.layout();
33681         
33682     },
33683     
33684     layout : function()
33685     {   
33686         this._resetLayout();
33687         
33688         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33689         
33690         this.layoutItems( isInstant );
33691       
33692         this._isLayoutInited = true;
33693         
33694         this.fireEvent('layout', this);
33695         
33696     },
33697     
33698     _resetLayout : function()
33699     {
33700         if(this.isHorizontal){
33701             this.horizontalMeasureColumns();
33702             return;
33703         }
33704         
33705         this.verticalMeasureColumns();
33706         
33707     },
33708     
33709     verticalMeasureColumns : function()
33710     {
33711         this.getContainerWidth();
33712         
33713 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33714 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33715 //            return;
33716 //        }
33717         
33718         var boxWidth = this.boxWidth + this.padWidth;
33719         
33720         if(this.containerWidth < this.boxWidth){
33721             boxWidth = this.containerWidth
33722         }
33723         
33724         var containerWidth = this.containerWidth;
33725         
33726         var cols = Math.floor(containerWidth / boxWidth);
33727         
33728         this.cols = Math.max( cols, 1 );
33729         
33730         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33731         
33732         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33733         
33734         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33735         
33736         this.colWidth = boxWidth + avail - this.padWidth;
33737         
33738         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33739         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33740     },
33741     
33742     horizontalMeasureColumns : function()
33743     {
33744         this.getContainerWidth();
33745         
33746         var boxWidth = this.boxWidth;
33747         
33748         if(this.containerWidth < boxWidth){
33749             boxWidth = this.containerWidth;
33750         }
33751         
33752         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33753         
33754         this.el.setHeight(boxWidth);
33755         
33756     },
33757     
33758     getContainerWidth : function()
33759     {
33760         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33761     },
33762     
33763     layoutItems : function( isInstant )
33764     {
33765         Roo.log(this.bricks);
33766         
33767         var items = Roo.apply([], this.bricks);
33768         
33769         if(this.isHorizontal){
33770             this._horizontalLayoutItems( items , isInstant );
33771             return;
33772         }
33773         
33774 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33775 //            this._verticalAlternativeLayoutItems( items , isInstant );
33776 //            return;
33777 //        }
33778         
33779         this._verticalLayoutItems( items , isInstant );
33780         
33781     },
33782     
33783     _verticalLayoutItems : function ( items , isInstant)
33784     {
33785         if ( !items || !items.length ) {
33786             return;
33787         }
33788         
33789         var standard = [
33790             ['xs', 'xs', 'xs', 'tall'],
33791             ['xs', 'xs', 'tall'],
33792             ['xs', 'xs', 'sm'],
33793             ['xs', 'xs', 'xs'],
33794             ['xs', 'tall'],
33795             ['xs', 'sm'],
33796             ['xs', 'xs'],
33797             ['xs'],
33798             
33799             ['sm', 'xs', 'xs'],
33800             ['sm', 'xs'],
33801             ['sm'],
33802             
33803             ['tall', 'xs', 'xs', 'xs'],
33804             ['tall', 'xs', 'xs'],
33805             ['tall', 'xs'],
33806             ['tall']
33807             
33808         ];
33809         
33810         var queue = [];
33811         
33812         var boxes = [];
33813         
33814         var box = [];
33815         
33816         Roo.each(items, function(item, k){
33817             
33818             switch (item.size) {
33819                 // these layouts take up a full box,
33820                 case 'md' :
33821                 case 'md-left' :
33822                 case 'md-right' :
33823                 case 'wide' :
33824                     
33825                     if(box.length){
33826                         boxes.push(box);
33827                         box = [];
33828                     }
33829                     
33830                     boxes.push([item]);
33831                     
33832                     break;
33833                     
33834                 case 'xs' :
33835                 case 'sm' :
33836                 case 'tall' :
33837                     
33838                     box.push(item);
33839                     
33840                     break;
33841                 default :
33842                     break;
33843                     
33844             }
33845             
33846         }, this);
33847         
33848         if(box.length){
33849             boxes.push(box);
33850             box = [];
33851         }
33852         
33853         var filterPattern = function(box, length)
33854         {
33855             if(!box.length){
33856                 return;
33857             }
33858             
33859             var match = false;
33860             
33861             var pattern = box.slice(0, length);
33862             
33863             var format = [];
33864             
33865             Roo.each(pattern, function(i){
33866                 format.push(i.size);
33867             }, this);
33868             
33869             Roo.each(standard, function(s){
33870                 
33871                 if(String(s) != String(format)){
33872                     return;
33873                 }
33874                 
33875                 match = true;
33876                 return false;
33877                 
33878             }, this);
33879             
33880             if(!match && length == 1){
33881                 return;
33882             }
33883             
33884             if(!match){
33885                 filterPattern(box, length - 1);
33886                 return;
33887             }
33888                 
33889             queue.push(pattern);
33890
33891             box = box.slice(length, box.length);
33892
33893             filterPattern(box, 4);
33894
33895             return;
33896             
33897         }
33898         
33899         Roo.each(boxes, function(box, k){
33900             
33901             if(!box.length){
33902                 return;
33903             }
33904             
33905             if(box.length == 1){
33906                 queue.push(box);
33907                 return;
33908             }
33909             
33910             filterPattern(box, 4);
33911             
33912         }, this);
33913         
33914         this._processVerticalLayoutQueue( queue, isInstant );
33915         
33916     },
33917     
33918 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33919 //    {
33920 //        if ( !items || !items.length ) {
33921 //            return;
33922 //        }
33923 //
33924 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33925 //        
33926 //    },
33927     
33928     _horizontalLayoutItems : function ( items , isInstant)
33929     {
33930         if ( !items || !items.length || items.length < 3) {
33931             return;
33932         }
33933         
33934         items.reverse();
33935         
33936         var eItems = items.slice(0, 3);
33937         
33938         items = items.slice(3, items.length);
33939         
33940         var standard = [
33941             ['xs', 'xs', 'xs', 'wide'],
33942             ['xs', 'xs', 'wide'],
33943             ['xs', 'xs', 'sm'],
33944             ['xs', 'xs', 'xs'],
33945             ['xs', 'wide'],
33946             ['xs', 'sm'],
33947             ['xs', 'xs'],
33948             ['xs'],
33949             
33950             ['sm', 'xs', 'xs'],
33951             ['sm', 'xs'],
33952             ['sm'],
33953             
33954             ['wide', 'xs', 'xs', 'xs'],
33955             ['wide', 'xs', 'xs'],
33956             ['wide', 'xs'],
33957             ['wide'],
33958             
33959             ['wide-thin']
33960         ];
33961         
33962         var queue = [];
33963         
33964         var boxes = [];
33965         
33966         var box = [];
33967         
33968         Roo.each(items, function(item, k){
33969             
33970             switch (item.size) {
33971                 case 'md' :
33972                 case 'md-left' :
33973                 case 'md-right' :
33974                 case 'tall' :
33975                     
33976                     if(box.length){
33977                         boxes.push(box);
33978                         box = [];
33979                     }
33980                     
33981                     boxes.push([item]);
33982                     
33983                     break;
33984                     
33985                 case 'xs' :
33986                 case 'sm' :
33987                 case 'wide' :
33988                 case 'wide-thin' :
33989                     
33990                     box.push(item);
33991                     
33992                     break;
33993                 default :
33994                     break;
33995                     
33996             }
33997             
33998         }, this);
33999         
34000         if(box.length){
34001             boxes.push(box);
34002             box = [];
34003         }
34004         
34005         var filterPattern = function(box, length)
34006         {
34007             if(!box.length){
34008                 return;
34009             }
34010             
34011             var match = false;
34012             
34013             var pattern = box.slice(0, length);
34014             
34015             var format = [];
34016             
34017             Roo.each(pattern, function(i){
34018                 format.push(i.size);
34019             }, this);
34020             
34021             Roo.each(standard, function(s){
34022                 
34023                 if(String(s) != String(format)){
34024                     return;
34025                 }
34026                 
34027                 match = true;
34028                 return false;
34029                 
34030             }, this);
34031             
34032             if(!match && length == 1){
34033                 return;
34034             }
34035             
34036             if(!match){
34037                 filterPattern(box, length - 1);
34038                 return;
34039             }
34040                 
34041             queue.push(pattern);
34042
34043             box = box.slice(length, box.length);
34044
34045             filterPattern(box, 4);
34046
34047             return;
34048             
34049         }
34050         
34051         Roo.each(boxes, function(box, k){
34052             
34053             if(!box.length){
34054                 return;
34055             }
34056             
34057             if(box.length == 1){
34058                 queue.push(box);
34059                 return;
34060             }
34061             
34062             filterPattern(box, 4);
34063             
34064         }, this);
34065         
34066         
34067         var prune = [];
34068         
34069         var pos = this.el.getBox(true);
34070         
34071         var minX = pos.x;
34072         
34073         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34074         
34075         var hit_end = false;
34076         
34077         Roo.each(queue, function(box){
34078             
34079             if(hit_end){
34080                 
34081                 Roo.each(box, function(b){
34082                 
34083                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34084                     b.el.hide();
34085
34086                 }, this);
34087
34088                 return;
34089             }
34090             
34091             var mx = 0;
34092             
34093             Roo.each(box, function(b){
34094                 
34095                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34096                 b.el.show();
34097
34098                 mx = Math.max(mx, b.x);
34099                 
34100             }, this);
34101             
34102             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34103             
34104             if(maxX < minX){
34105                 
34106                 Roo.each(box, function(b){
34107                 
34108                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34109                     b.el.hide();
34110                     
34111                 }, this);
34112                 
34113                 hit_end = true;
34114                 
34115                 return;
34116             }
34117             
34118             prune.push(box);
34119             
34120         }, this);
34121         
34122         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34123     },
34124     
34125     /** Sets position of item in DOM
34126     * @param {Element} item
34127     * @param {Number} x - horizontal position
34128     * @param {Number} y - vertical position
34129     * @param {Boolean} isInstant - disables transitions
34130     */
34131     _processVerticalLayoutQueue : function( queue, isInstant )
34132     {
34133         var pos = this.el.getBox(true);
34134         var x = pos.x;
34135         var y = pos.y;
34136         var maxY = [];
34137         
34138         for (var i = 0; i < this.cols; i++){
34139             maxY[i] = pos.y;
34140         }
34141         
34142         Roo.each(queue, function(box, k){
34143             
34144             var col = k % this.cols;
34145             
34146             Roo.each(box, function(b,kk){
34147                 
34148                 b.el.position('absolute');
34149                 
34150                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34151                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34152                 
34153                 if(b.size == 'md-left' || b.size == 'md-right'){
34154                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34155                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34156                 }
34157                 
34158                 b.el.setWidth(width);
34159                 b.el.setHeight(height);
34160                 // iframe?
34161                 b.el.select('iframe',true).setSize(width,height);
34162                 
34163             }, this);
34164             
34165             for (var i = 0; i < this.cols; i++){
34166                 
34167                 if(maxY[i] < maxY[col]){
34168                     col = i;
34169                     continue;
34170                 }
34171                 
34172                 col = Math.min(col, i);
34173                 
34174             }
34175             
34176             x = pos.x + col * (this.colWidth + this.padWidth);
34177             
34178             y = maxY[col];
34179             
34180             var positions = [];
34181             
34182             switch (box.length){
34183                 case 1 :
34184                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34185                     break;
34186                 case 2 :
34187                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34188                     break;
34189                 case 3 :
34190                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34191                     break;
34192                 case 4 :
34193                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34194                     break;
34195                 default :
34196                     break;
34197             }
34198             
34199             Roo.each(box, function(b,kk){
34200                 
34201                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34202                 
34203                 var sz = b.el.getSize();
34204                 
34205                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34206                 
34207             }, this);
34208             
34209         }, this);
34210         
34211         var mY = 0;
34212         
34213         for (var i = 0; i < this.cols; i++){
34214             mY = Math.max(mY, maxY[i]);
34215         }
34216         
34217         this.el.setHeight(mY - pos.y);
34218         
34219     },
34220     
34221 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34222 //    {
34223 //        var pos = this.el.getBox(true);
34224 //        var x = pos.x;
34225 //        var y = pos.y;
34226 //        var maxX = pos.right;
34227 //        
34228 //        var maxHeight = 0;
34229 //        
34230 //        Roo.each(items, function(item, k){
34231 //            
34232 //            var c = k % 2;
34233 //            
34234 //            item.el.position('absolute');
34235 //                
34236 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34237 //
34238 //            item.el.setWidth(width);
34239 //
34240 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34241 //
34242 //            item.el.setHeight(height);
34243 //            
34244 //            if(c == 0){
34245 //                item.el.setXY([x, y], isInstant ? false : true);
34246 //            } else {
34247 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34248 //            }
34249 //            
34250 //            y = y + height + this.alternativePadWidth;
34251 //            
34252 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34253 //            
34254 //        }, this);
34255 //        
34256 //        this.el.setHeight(maxHeight);
34257 //        
34258 //    },
34259     
34260     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34261     {
34262         var pos = this.el.getBox(true);
34263         
34264         var minX = pos.x;
34265         var minY = pos.y;
34266         
34267         var maxX = pos.right;
34268         
34269         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34270         
34271         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34272         
34273         Roo.each(queue, function(box, k){
34274             
34275             Roo.each(box, function(b, kk){
34276                 
34277                 b.el.position('absolute');
34278                 
34279                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34280                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34281                 
34282                 if(b.size == 'md-left' || b.size == 'md-right'){
34283                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34284                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34285                 }
34286                 
34287                 b.el.setWidth(width);
34288                 b.el.setHeight(height);
34289                 
34290             }, this);
34291             
34292             if(!box.length){
34293                 return;
34294             }
34295             
34296             var positions = [];
34297             
34298             switch (box.length){
34299                 case 1 :
34300                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34301                     break;
34302                 case 2 :
34303                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34304                     break;
34305                 case 3 :
34306                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34307                     break;
34308                 case 4 :
34309                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34310                     break;
34311                 default :
34312                     break;
34313             }
34314             
34315             Roo.each(box, function(b,kk){
34316                 
34317                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34318                 
34319                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34320                 
34321             }, this);
34322             
34323         }, this);
34324         
34325     },
34326     
34327     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34328     {
34329         Roo.each(eItems, function(b,k){
34330             
34331             b.size = (k == 0) ? 'sm' : 'xs';
34332             b.x = (k == 0) ? 2 : 1;
34333             b.y = (k == 0) ? 2 : 1;
34334             
34335             b.el.position('absolute');
34336             
34337             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34338                 
34339             b.el.setWidth(width);
34340             
34341             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34342             
34343             b.el.setHeight(height);
34344             
34345         }, this);
34346
34347         var positions = [];
34348         
34349         positions.push({
34350             x : maxX - this.unitWidth * 2 - this.gutter,
34351             y : minY
34352         });
34353         
34354         positions.push({
34355             x : maxX - this.unitWidth,
34356             y : minY + (this.unitWidth + this.gutter) * 2
34357         });
34358         
34359         positions.push({
34360             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34361             y : minY
34362         });
34363         
34364         Roo.each(eItems, function(b,k){
34365             
34366             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34367
34368         }, this);
34369         
34370     },
34371     
34372     getVerticalOneBoxColPositions : function(x, y, box)
34373     {
34374         var pos = [];
34375         
34376         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34377         
34378         if(box[0].size == 'md-left'){
34379             rand = 0;
34380         }
34381         
34382         if(box[0].size == 'md-right'){
34383             rand = 1;
34384         }
34385         
34386         pos.push({
34387             x : x + (this.unitWidth + this.gutter) * rand,
34388             y : y
34389         });
34390         
34391         return pos;
34392     },
34393     
34394     getVerticalTwoBoxColPositions : function(x, y, box)
34395     {
34396         var pos = [];
34397         
34398         if(box[0].size == 'xs'){
34399             
34400             pos.push({
34401                 x : x,
34402                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34403             });
34404
34405             pos.push({
34406                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34407                 y : y
34408             });
34409             
34410             return pos;
34411             
34412         }
34413         
34414         pos.push({
34415             x : x,
34416             y : y
34417         });
34418
34419         pos.push({
34420             x : x + (this.unitWidth + this.gutter) * 2,
34421             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34422         });
34423         
34424         return pos;
34425         
34426     },
34427     
34428     getVerticalThreeBoxColPositions : function(x, y, box)
34429     {
34430         var pos = [];
34431         
34432         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34433             
34434             pos.push({
34435                 x : x,
34436                 y : y
34437             });
34438
34439             pos.push({
34440                 x : x + (this.unitWidth + this.gutter) * 1,
34441                 y : y
34442             });
34443             
34444             pos.push({
34445                 x : x + (this.unitWidth + this.gutter) * 2,
34446                 y : y
34447             });
34448             
34449             return pos;
34450             
34451         }
34452         
34453         if(box[0].size == 'xs' && box[1].size == 'xs'){
34454             
34455             pos.push({
34456                 x : x,
34457                 y : y
34458             });
34459
34460             pos.push({
34461                 x : x,
34462                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34463             });
34464             
34465             pos.push({
34466                 x : x + (this.unitWidth + this.gutter) * 1,
34467                 y : y
34468             });
34469             
34470             return pos;
34471             
34472         }
34473         
34474         pos.push({
34475             x : x,
34476             y : y
34477         });
34478
34479         pos.push({
34480             x : x + (this.unitWidth + this.gutter) * 2,
34481             y : y
34482         });
34483
34484         pos.push({
34485             x : x + (this.unitWidth + this.gutter) * 2,
34486             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34487         });
34488             
34489         return pos;
34490         
34491     },
34492     
34493     getVerticalFourBoxColPositions : function(x, y, box)
34494     {
34495         var pos = [];
34496         
34497         if(box[0].size == 'xs'){
34498             
34499             pos.push({
34500                 x : x,
34501                 y : y
34502             });
34503
34504             pos.push({
34505                 x : x,
34506                 y : y + (this.unitHeight + this.gutter) * 1
34507             });
34508             
34509             pos.push({
34510                 x : x,
34511                 y : y + (this.unitHeight + this.gutter) * 2
34512             });
34513             
34514             pos.push({
34515                 x : x + (this.unitWidth + this.gutter) * 1,
34516                 y : y
34517             });
34518             
34519             return pos;
34520             
34521         }
34522         
34523         pos.push({
34524             x : x,
34525             y : y
34526         });
34527
34528         pos.push({
34529             x : x + (this.unitWidth + this.gutter) * 2,
34530             y : y
34531         });
34532
34533         pos.push({
34534             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34535             y : y + (this.unitHeight + this.gutter) * 1
34536         });
34537
34538         pos.push({
34539             x : x + (this.unitWidth + this.gutter) * 2,
34540             y : y + (this.unitWidth + this.gutter) * 2
34541         });
34542
34543         return pos;
34544         
34545     },
34546     
34547     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34548     {
34549         var pos = [];
34550         
34551         if(box[0].size == 'md-left'){
34552             pos.push({
34553                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34554                 y : minY
34555             });
34556             
34557             return pos;
34558         }
34559         
34560         if(box[0].size == 'md-right'){
34561             pos.push({
34562                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34563                 y : minY + (this.unitWidth + this.gutter) * 1
34564             });
34565             
34566             return pos;
34567         }
34568         
34569         var rand = Math.floor(Math.random() * (4 - box[0].y));
34570         
34571         pos.push({
34572             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34573             y : minY + (this.unitWidth + this.gutter) * rand
34574         });
34575         
34576         return pos;
34577         
34578     },
34579     
34580     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34581     {
34582         var pos = [];
34583         
34584         if(box[0].size == 'xs'){
34585             
34586             pos.push({
34587                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34588                 y : minY
34589             });
34590
34591             pos.push({
34592                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34593                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34594             });
34595             
34596             return pos;
34597             
34598         }
34599         
34600         pos.push({
34601             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34602             y : minY
34603         });
34604
34605         pos.push({
34606             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34607             y : minY + (this.unitWidth + this.gutter) * 2
34608         });
34609         
34610         return pos;
34611         
34612     },
34613     
34614     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34615     {
34616         var pos = [];
34617         
34618         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34619             
34620             pos.push({
34621                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34622                 y : minY
34623             });
34624
34625             pos.push({
34626                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34627                 y : minY + (this.unitWidth + this.gutter) * 1
34628             });
34629             
34630             pos.push({
34631                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34632                 y : minY + (this.unitWidth + this.gutter) * 2
34633             });
34634             
34635             return pos;
34636             
34637         }
34638         
34639         if(box[0].size == 'xs' && box[1].size == 'xs'){
34640             
34641             pos.push({
34642                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34643                 y : minY
34644             });
34645
34646             pos.push({
34647                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34648                 y : minY
34649             });
34650             
34651             pos.push({
34652                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34653                 y : minY + (this.unitWidth + this.gutter) * 1
34654             });
34655             
34656             return pos;
34657             
34658         }
34659         
34660         pos.push({
34661             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34662             y : minY
34663         });
34664
34665         pos.push({
34666             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34667             y : minY + (this.unitWidth + this.gutter) * 2
34668         });
34669
34670         pos.push({
34671             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34672             y : minY + (this.unitWidth + this.gutter) * 2
34673         });
34674             
34675         return pos;
34676         
34677     },
34678     
34679     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34680     {
34681         var pos = [];
34682         
34683         if(box[0].size == 'xs'){
34684             
34685             pos.push({
34686                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34687                 y : minY
34688             });
34689
34690             pos.push({
34691                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34692                 y : minY
34693             });
34694             
34695             pos.push({
34696                 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),
34697                 y : minY
34698             });
34699             
34700             pos.push({
34701                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34702                 y : minY + (this.unitWidth + this.gutter) * 1
34703             });
34704             
34705             return pos;
34706             
34707         }
34708         
34709         pos.push({
34710             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34711             y : minY
34712         });
34713         
34714         pos.push({
34715             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34716             y : minY + (this.unitWidth + this.gutter) * 2
34717         });
34718         
34719         pos.push({
34720             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34721             y : minY + (this.unitWidth + this.gutter) * 2
34722         });
34723         
34724         pos.push({
34725             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),
34726             y : minY + (this.unitWidth + this.gutter) * 2
34727         });
34728
34729         return pos;
34730         
34731     },
34732     
34733     /**
34734     * remove a Masonry Brick
34735     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34736     */
34737     removeBrick : function(brick_id)
34738     {
34739         if (!brick_id) {
34740             return;
34741         }
34742         
34743         for (var i = 0; i<this.bricks.length; i++) {
34744             if (this.bricks[i].id == brick_id) {
34745                 this.bricks.splice(i,1);
34746                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34747                 this.initial();
34748             }
34749         }
34750     },
34751     
34752     /**
34753     * adds a Masonry Brick
34754     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34755     */
34756     addBrick : function(cfg)
34757     {
34758         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34759         //this.register(cn);
34760         cn.parentId = this.id;
34761         cn.render(this.el);
34762         return cn;
34763     },
34764     
34765     /**
34766     * register a Masonry Brick
34767     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34768     */
34769     
34770     register : function(brick)
34771     {
34772         this.bricks.push(brick);
34773         brick.masonryId = this.id;
34774     },
34775     
34776     /**
34777     * clear all the Masonry Brick
34778     */
34779     clearAll : function()
34780     {
34781         this.bricks = [];
34782         //this.getChildContainer().dom.innerHTML = "";
34783         this.el.dom.innerHTML = '';
34784     },
34785     
34786     getSelected : function()
34787     {
34788         if (!this.selectedBrick) {
34789             return false;
34790         }
34791         
34792         return this.selectedBrick;
34793     }
34794 });
34795
34796 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34797     
34798     groups: {},
34799      /**
34800     * register a Masonry Layout
34801     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34802     */
34803     
34804     register : function(layout)
34805     {
34806         this.groups[layout.id] = layout;
34807     },
34808     /**
34809     * fetch a  Masonry Layout based on the masonry layout ID
34810     * @param {string} the masonry layout to add
34811     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34812     */
34813     
34814     get: function(layout_id) {
34815         if (typeof(this.groups[layout_id]) == 'undefined') {
34816             return false;
34817         }
34818         return this.groups[layout_id] ;
34819     }
34820     
34821     
34822     
34823 });
34824
34825  
34826
34827  /**
34828  *
34829  * This is based on 
34830  * http://masonry.desandro.com
34831  *
34832  * The idea is to render all the bricks based on vertical width...
34833  *
34834  * The original code extends 'outlayer' - we might need to use that....
34835  * 
34836  */
34837
34838
34839 /**
34840  * @class Roo.bootstrap.LayoutMasonryAuto
34841  * @extends Roo.bootstrap.Component
34842  * Bootstrap Layout Masonry class
34843  * 
34844  * @constructor
34845  * Create a new Element
34846  * @param {Object} config The config object
34847  */
34848
34849 Roo.bootstrap.LayoutMasonryAuto = function(config){
34850     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34851 };
34852
34853 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34854     
34855       /**
34856      * @cfg {Boolean} isFitWidth  - resize the width..
34857      */   
34858     isFitWidth : false,  // options..
34859     /**
34860      * @cfg {Boolean} isOriginLeft = left align?
34861      */   
34862     isOriginLeft : true,
34863     /**
34864      * @cfg {Boolean} isOriginTop = top align?
34865      */   
34866     isOriginTop : false,
34867     /**
34868      * @cfg {Boolean} isLayoutInstant = no animation?
34869      */   
34870     isLayoutInstant : false, // needed?
34871     /**
34872      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34873      */   
34874     isResizingContainer : true,
34875     /**
34876      * @cfg {Number} columnWidth  width of the columns 
34877      */   
34878     
34879     columnWidth : 0,
34880     
34881     /**
34882      * @cfg {Number} maxCols maximum number of columns
34883      */   
34884     
34885     maxCols: 0,
34886     /**
34887      * @cfg {Number} padHeight padding below box..
34888      */   
34889     
34890     padHeight : 10, 
34891     
34892     /**
34893      * @cfg {Boolean} isAutoInitial defalut true
34894      */   
34895     
34896     isAutoInitial : true, 
34897     
34898     // private?
34899     gutter : 0,
34900     
34901     containerWidth: 0,
34902     initialColumnWidth : 0,
34903     currentSize : null,
34904     
34905     colYs : null, // array.
34906     maxY : 0,
34907     padWidth: 10,
34908     
34909     
34910     tag: 'div',
34911     cls: '',
34912     bricks: null, //CompositeElement
34913     cols : 0, // array?
34914     // element : null, // wrapped now this.el
34915     _isLayoutInited : null, 
34916     
34917     
34918     getAutoCreate : function(){
34919         
34920         var cfg = {
34921             tag: this.tag,
34922             cls: 'blog-masonary-wrapper ' + this.cls,
34923             cn : {
34924                 cls : 'mas-boxes masonary'
34925             }
34926         };
34927         
34928         return cfg;
34929     },
34930     
34931     getChildContainer: function( )
34932     {
34933         if (this.boxesEl) {
34934             return this.boxesEl;
34935         }
34936         
34937         this.boxesEl = this.el.select('.mas-boxes').first();
34938         
34939         return this.boxesEl;
34940     },
34941     
34942     
34943     initEvents : function()
34944     {
34945         var _this = this;
34946         
34947         if(this.isAutoInitial){
34948             Roo.log('hook children rendered');
34949             this.on('childrenrendered', function() {
34950                 Roo.log('children rendered');
34951                 _this.initial();
34952             } ,this);
34953         }
34954         
34955     },
34956     
34957     initial : function()
34958     {
34959         this.reloadItems();
34960
34961         this.currentSize = this.el.getBox(true);
34962
34963         /// was window resize... - let's see if this works..
34964         Roo.EventManager.onWindowResize(this.resize, this); 
34965
34966         if(!this.isAutoInitial){
34967             this.layout();
34968             return;
34969         }
34970         
34971         this.layout.defer(500,this);
34972     },
34973     
34974     reloadItems: function()
34975     {
34976         this.bricks = this.el.select('.masonry-brick', true);
34977         
34978         this.bricks.each(function(b) {
34979             //Roo.log(b.getSize());
34980             if (!b.attr('originalwidth')) {
34981                 b.attr('originalwidth',  b.getSize().width);
34982             }
34983             
34984         });
34985         
34986         Roo.log(this.bricks.elements.length);
34987     },
34988     
34989     resize : function()
34990     {
34991         Roo.log('resize');
34992         var cs = this.el.getBox(true);
34993         
34994         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34995             Roo.log("no change in with or X");
34996             return;
34997         }
34998         this.currentSize = cs;
34999         this.layout();
35000     },
35001     
35002     layout : function()
35003     {
35004          Roo.log('layout');
35005         this._resetLayout();
35006         //this._manageStamps();
35007       
35008         // don't animate first layout
35009         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35010         this.layoutItems( isInstant );
35011       
35012         // flag for initalized
35013         this._isLayoutInited = true;
35014     },
35015     
35016     layoutItems : function( isInstant )
35017     {
35018         //var items = this._getItemsForLayout( this.items );
35019         // original code supports filtering layout items.. we just ignore it..
35020         
35021         this._layoutItems( this.bricks , isInstant );
35022       
35023         this._postLayout();
35024     },
35025     _layoutItems : function ( items , isInstant)
35026     {
35027        //this.fireEvent( 'layout', this, items );
35028     
35029
35030         if ( !items || !items.elements.length ) {
35031           // no items, emit event with empty array
35032             return;
35033         }
35034
35035         var queue = [];
35036         items.each(function(item) {
35037             Roo.log("layout item");
35038             Roo.log(item);
35039             // get x/y object from method
35040             var position = this._getItemLayoutPosition( item );
35041             // enqueue
35042             position.item = item;
35043             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35044             queue.push( position );
35045         }, this);
35046       
35047         this._processLayoutQueue( queue );
35048     },
35049     /** Sets position of item in DOM
35050     * @param {Element} item
35051     * @param {Number} x - horizontal position
35052     * @param {Number} y - vertical position
35053     * @param {Boolean} isInstant - disables transitions
35054     */
35055     _processLayoutQueue : function( queue )
35056     {
35057         for ( var i=0, len = queue.length; i < len; i++ ) {
35058             var obj = queue[i];
35059             obj.item.position('absolute');
35060             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35061         }
35062     },
35063       
35064     
35065     /**
35066     * Any logic you want to do after each layout,
35067     * i.e. size the container
35068     */
35069     _postLayout : function()
35070     {
35071         this.resizeContainer();
35072     },
35073     
35074     resizeContainer : function()
35075     {
35076         if ( !this.isResizingContainer ) {
35077             return;
35078         }
35079         var size = this._getContainerSize();
35080         if ( size ) {
35081             this.el.setSize(size.width,size.height);
35082             this.boxesEl.setSize(size.width,size.height);
35083         }
35084     },
35085     
35086     
35087     
35088     _resetLayout : function()
35089     {
35090         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35091         this.colWidth = this.el.getWidth();
35092         //this.gutter = this.el.getWidth(); 
35093         
35094         this.measureColumns();
35095
35096         // reset column Y
35097         var i = this.cols;
35098         this.colYs = [];
35099         while (i--) {
35100             this.colYs.push( 0 );
35101         }
35102     
35103         this.maxY = 0;
35104     },
35105
35106     measureColumns : function()
35107     {
35108         this.getContainerWidth();
35109       // if columnWidth is 0, default to outerWidth of first item
35110         if ( !this.columnWidth ) {
35111             var firstItem = this.bricks.first();
35112             Roo.log(firstItem);
35113             this.columnWidth  = this.containerWidth;
35114             if (firstItem && firstItem.attr('originalwidth') ) {
35115                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35116             }
35117             // columnWidth fall back to item of first element
35118             Roo.log("set column width?");
35119                         this.initialColumnWidth = this.columnWidth  ;
35120
35121             // if first elem has no width, default to size of container
35122             
35123         }
35124         
35125         
35126         if (this.initialColumnWidth) {
35127             this.columnWidth = this.initialColumnWidth;
35128         }
35129         
35130         
35131             
35132         // column width is fixed at the top - however if container width get's smaller we should
35133         // reduce it...
35134         
35135         // this bit calcs how man columns..
35136             
35137         var columnWidth = this.columnWidth += this.gutter;
35138       
35139         // calculate columns
35140         var containerWidth = this.containerWidth + this.gutter;
35141         
35142         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35143         // fix rounding errors, typically with gutters
35144         var excess = columnWidth - containerWidth % columnWidth;
35145         
35146         
35147         // if overshoot is less than a pixel, round up, otherwise floor it
35148         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35149         cols = Math[ mathMethod ]( cols );
35150         this.cols = Math.max( cols, 1 );
35151         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35152         
35153          // padding positioning..
35154         var totalColWidth = this.cols * this.columnWidth;
35155         var padavail = this.containerWidth - totalColWidth;
35156         // so for 2 columns - we need 3 'pads'
35157         
35158         var padNeeded = (1+this.cols) * this.padWidth;
35159         
35160         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35161         
35162         this.columnWidth += padExtra
35163         //this.padWidth = Math.floor(padavail /  ( this.cols));
35164         
35165         // adjust colum width so that padding is fixed??
35166         
35167         // we have 3 columns ... total = width * 3
35168         // we have X left over... that should be used by 
35169         
35170         //if (this.expandC) {
35171             
35172         //}
35173         
35174         
35175         
35176     },
35177     
35178     getContainerWidth : function()
35179     {
35180        /* // container is parent if fit width
35181         var container = this.isFitWidth ? this.element.parentNode : this.element;
35182         // check that this.size and size are there
35183         // IE8 triggers resize on body size change, so they might not be
35184         
35185         var size = getSize( container );  //FIXME
35186         this.containerWidth = size && size.innerWidth; //FIXME
35187         */
35188          
35189         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35190         
35191     },
35192     
35193     _getItemLayoutPosition : function( item )  // what is item?
35194     {
35195         // we resize the item to our columnWidth..
35196       
35197         item.setWidth(this.columnWidth);
35198         item.autoBoxAdjust  = false;
35199         
35200         var sz = item.getSize();
35201  
35202         // how many columns does this brick span
35203         var remainder = this.containerWidth % this.columnWidth;
35204         
35205         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35206         // round if off by 1 pixel, otherwise use ceil
35207         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35208         colSpan = Math.min( colSpan, this.cols );
35209         
35210         // normally this should be '1' as we dont' currently allow multi width columns..
35211         
35212         var colGroup = this._getColGroup( colSpan );
35213         // get the minimum Y value from the columns
35214         var minimumY = Math.min.apply( Math, colGroup );
35215         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35216         
35217         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35218          
35219         // position the brick
35220         var position = {
35221             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35222             y: this.currentSize.y + minimumY + this.padHeight
35223         };
35224         
35225         Roo.log(position);
35226         // apply setHeight to necessary columns
35227         var setHeight = minimumY + sz.height + this.padHeight;
35228         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35229         
35230         var setSpan = this.cols + 1 - colGroup.length;
35231         for ( var i = 0; i < setSpan; i++ ) {
35232           this.colYs[ shortColIndex + i ] = setHeight ;
35233         }
35234       
35235         return position;
35236     },
35237     
35238     /**
35239      * @param {Number} colSpan - number of columns the element spans
35240      * @returns {Array} colGroup
35241      */
35242     _getColGroup : function( colSpan )
35243     {
35244         if ( colSpan < 2 ) {
35245           // if brick spans only one column, use all the column Ys
35246           return this.colYs;
35247         }
35248       
35249         var colGroup = [];
35250         // how many different places could this brick fit horizontally
35251         var groupCount = this.cols + 1 - colSpan;
35252         // for each group potential horizontal position
35253         for ( var i = 0; i < groupCount; i++ ) {
35254           // make an array of colY values for that one group
35255           var groupColYs = this.colYs.slice( i, i + colSpan );
35256           // and get the max value of the array
35257           colGroup[i] = Math.max.apply( Math, groupColYs );
35258         }
35259         return colGroup;
35260     },
35261     /*
35262     _manageStamp : function( stamp )
35263     {
35264         var stampSize =  stamp.getSize();
35265         var offset = stamp.getBox();
35266         // get the columns that this stamp affects
35267         var firstX = this.isOriginLeft ? offset.x : offset.right;
35268         var lastX = firstX + stampSize.width;
35269         var firstCol = Math.floor( firstX / this.columnWidth );
35270         firstCol = Math.max( 0, firstCol );
35271         
35272         var lastCol = Math.floor( lastX / this.columnWidth );
35273         // lastCol should not go over if multiple of columnWidth #425
35274         lastCol -= lastX % this.columnWidth ? 0 : 1;
35275         lastCol = Math.min( this.cols - 1, lastCol );
35276         
35277         // set colYs to bottom of the stamp
35278         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35279             stampSize.height;
35280             
35281         for ( var i = firstCol; i <= lastCol; i++ ) {
35282           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35283         }
35284     },
35285     */
35286     
35287     _getContainerSize : function()
35288     {
35289         this.maxY = Math.max.apply( Math, this.colYs );
35290         var size = {
35291             height: this.maxY
35292         };
35293       
35294         if ( this.isFitWidth ) {
35295             size.width = this._getContainerFitWidth();
35296         }
35297       
35298         return size;
35299     },
35300     
35301     _getContainerFitWidth : function()
35302     {
35303         var unusedCols = 0;
35304         // count unused columns
35305         var i = this.cols;
35306         while ( --i ) {
35307           if ( this.colYs[i] !== 0 ) {
35308             break;
35309           }
35310           unusedCols++;
35311         }
35312         // fit container to columns that have been used
35313         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35314     },
35315     
35316     needsResizeLayout : function()
35317     {
35318         var previousWidth = this.containerWidth;
35319         this.getContainerWidth();
35320         return previousWidth !== this.containerWidth;
35321     }
35322  
35323 });
35324
35325  
35326
35327  /*
35328  * - LGPL
35329  *
35330  * element
35331  * 
35332  */
35333
35334 /**
35335  * @class Roo.bootstrap.MasonryBrick
35336  * @extends Roo.bootstrap.Component
35337  * Bootstrap MasonryBrick class
35338  * 
35339  * @constructor
35340  * Create a new MasonryBrick
35341  * @param {Object} config The config object
35342  */
35343
35344 Roo.bootstrap.MasonryBrick = function(config){
35345     
35346     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35347     
35348     Roo.bootstrap.MasonryBrick.register(this);
35349     
35350     this.addEvents({
35351         // raw events
35352         /**
35353          * @event click
35354          * When a MasonryBrick is clcik
35355          * @param {Roo.bootstrap.MasonryBrick} this
35356          * @param {Roo.EventObject} e
35357          */
35358         "click" : true
35359     });
35360 };
35361
35362 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35363     
35364     /**
35365      * @cfg {String} title
35366      */   
35367     title : '',
35368     /**
35369      * @cfg {String} html
35370      */   
35371     html : '',
35372     /**
35373      * @cfg {String} bgimage
35374      */   
35375     bgimage : '',
35376     /**
35377      * @cfg {String} videourl
35378      */   
35379     videourl : '',
35380     /**
35381      * @cfg {String} cls
35382      */   
35383     cls : '',
35384     /**
35385      * @cfg {String} href
35386      */   
35387     href : '',
35388     /**
35389      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35390      */   
35391     size : 'xs',
35392     
35393     /**
35394      * @cfg {String} placetitle (center|bottom)
35395      */   
35396     placetitle : '',
35397     
35398     /**
35399      * @cfg {Boolean} isFitContainer defalut true
35400      */   
35401     isFitContainer : true, 
35402     
35403     /**
35404      * @cfg {Boolean} preventDefault defalut false
35405      */   
35406     preventDefault : false, 
35407     
35408     /**
35409      * @cfg {Boolean} inverse defalut false
35410      */   
35411     maskInverse : false, 
35412     
35413     getAutoCreate : function()
35414     {
35415         if(!this.isFitContainer){
35416             return this.getSplitAutoCreate();
35417         }
35418         
35419         var cls = 'masonry-brick masonry-brick-full';
35420         
35421         if(this.href.length){
35422             cls += ' masonry-brick-link';
35423         }
35424         
35425         if(this.bgimage.length){
35426             cls += ' masonry-brick-image';
35427         }
35428         
35429         if(this.maskInverse){
35430             cls += ' mask-inverse';
35431         }
35432         
35433         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35434             cls += ' enable-mask';
35435         }
35436         
35437         if(this.size){
35438             cls += ' masonry-' + this.size + '-brick';
35439         }
35440         
35441         if(this.placetitle.length){
35442             
35443             switch (this.placetitle) {
35444                 case 'center' :
35445                     cls += ' masonry-center-title';
35446                     break;
35447                 case 'bottom' :
35448                     cls += ' masonry-bottom-title';
35449                     break;
35450                 default:
35451                     break;
35452             }
35453             
35454         } else {
35455             if(!this.html.length && !this.bgimage.length){
35456                 cls += ' masonry-center-title';
35457             }
35458
35459             if(!this.html.length && this.bgimage.length){
35460                 cls += ' masonry-bottom-title';
35461             }
35462         }
35463         
35464         if(this.cls){
35465             cls += ' ' + this.cls;
35466         }
35467         
35468         var cfg = {
35469             tag: (this.href.length) ? 'a' : 'div',
35470             cls: cls,
35471             cn: [
35472                 {
35473                     tag: 'div',
35474                     cls: 'masonry-brick-mask'
35475                 },
35476                 {
35477                     tag: 'div',
35478                     cls: 'masonry-brick-paragraph',
35479                     cn: []
35480                 }
35481             ]
35482         };
35483         
35484         if(this.href.length){
35485             cfg.href = this.href;
35486         }
35487         
35488         var cn = cfg.cn[1].cn;
35489         
35490         if(this.title.length){
35491             cn.push({
35492                 tag: 'h4',
35493                 cls: 'masonry-brick-title',
35494                 html: this.title
35495             });
35496         }
35497         
35498         if(this.html.length){
35499             cn.push({
35500                 tag: 'p',
35501                 cls: 'masonry-brick-text',
35502                 html: this.html
35503             });
35504         }
35505         
35506         if (!this.title.length && !this.html.length) {
35507             cfg.cn[1].cls += ' hide';
35508         }
35509         
35510         if(this.bgimage.length){
35511             cfg.cn.push({
35512                 tag: 'img',
35513                 cls: 'masonry-brick-image-view',
35514                 src: this.bgimage
35515             });
35516         }
35517         
35518         if(this.videourl.length){
35519             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35520             // youtube support only?
35521             cfg.cn.push({
35522                 tag: 'iframe',
35523                 cls: 'masonry-brick-image-view',
35524                 src: vurl,
35525                 frameborder : 0,
35526                 allowfullscreen : true
35527             });
35528         }
35529         
35530         return cfg;
35531         
35532     },
35533     
35534     getSplitAutoCreate : function()
35535     {
35536         var cls = 'masonry-brick masonry-brick-split';
35537         
35538         if(this.href.length){
35539             cls += ' masonry-brick-link';
35540         }
35541         
35542         if(this.bgimage.length){
35543             cls += ' masonry-brick-image';
35544         }
35545         
35546         if(this.size){
35547             cls += ' masonry-' + this.size + '-brick';
35548         }
35549         
35550         switch (this.placetitle) {
35551             case 'center' :
35552                 cls += ' masonry-center-title';
35553                 break;
35554             case 'bottom' :
35555                 cls += ' masonry-bottom-title';
35556                 break;
35557             default:
35558                 if(!this.bgimage.length){
35559                     cls += ' masonry-center-title';
35560                 }
35561
35562                 if(this.bgimage.length){
35563                     cls += ' masonry-bottom-title';
35564                 }
35565                 break;
35566         }
35567         
35568         if(this.cls){
35569             cls += ' ' + this.cls;
35570         }
35571         
35572         var cfg = {
35573             tag: (this.href.length) ? 'a' : 'div',
35574             cls: cls,
35575             cn: [
35576                 {
35577                     tag: 'div',
35578                     cls: 'masonry-brick-split-head',
35579                     cn: [
35580                         {
35581                             tag: 'div',
35582                             cls: 'masonry-brick-paragraph',
35583                             cn: []
35584                         }
35585                     ]
35586                 },
35587                 {
35588                     tag: 'div',
35589                     cls: 'masonry-brick-split-body',
35590                     cn: []
35591                 }
35592             ]
35593         };
35594         
35595         if(this.href.length){
35596             cfg.href = this.href;
35597         }
35598         
35599         if(this.title.length){
35600             cfg.cn[0].cn[0].cn.push({
35601                 tag: 'h4',
35602                 cls: 'masonry-brick-title',
35603                 html: this.title
35604             });
35605         }
35606         
35607         if(this.html.length){
35608             cfg.cn[1].cn.push({
35609                 tag: 'p',
35610                 cls: 'masonry-brick-text',
35611                 html: this.html
35612             });
35613         }
35614
35615         if(this.bgimage.length){
35616             cfg.cn[0].cn.push({
35617                 tag: 'img',
35618                 cls: 'masonry-brick-image-view',
35619                 src: this.bgimage
35620             });
35621         }
35622         
35623         if(this.videourl.length){
35624             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35625             // youtube support only?
35626             cfg.cn[0].cn.cn.push({
35627                 tag: 'iframe',
35628                 cls: 'masonry-brick-image-view',
35629                 src: vurl,
35630                 frameborder : 0,
35631                 allowfullscreen : true
35632             });
35633         }
35634         
35635         return cfg;
35636     },
35637     
35638     initEvents: function() 
35639     {
35640         switch (this.size) {
35641             case 'xs' :
35642                 this.x = 1;
35643                 this.y = 1;
35644                 break;
35645             case 'sm' :
35646                 this.x = 2;
35647                 this.y = 2;
35648                 break;
35649             case 'md' :
35650             case 'md-left' :
35651             case 'md-right' :
35652                 this.x = 3;
35653                 this.y = 3;
35654                 break;
35655             case 'tall' :
35656                 this.x = 2;
35657                 this.y = 3;
35658                 break;
35659             case 'wide' :
35660                 this.x = 3;
35661                 this.y = 2;
35662                 break;
35663             case 'wide-thin' :
35664                 this.x = 3;
35665                 this.y = 1;
35666                 break;
35667                         
35668             default :
35669                 break;
35670         }
35671         
35672         if(Roo.isTouch){
35673             this.el.on('touchstart', this.onTouchStart, this);
35674             this.el.on('touchmove', this.onTouchMove, this);
35675             this.el.on('touchend', this.onTouchEnd, this);
35676             this.el.on('contextmenu', this.onContextMenu, this);
35677         } else {
35678             this.el.on('mouseenter'  ,this.enter, this);
35679             this.el.on('mouseleave', this.leave, this);
35680             this.el.on('click', this.onClick, this);
35681         }
35682         
35683         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35684             this.parent().bricks.push(this);   
35685         }
35686         
35687     },
35688     
35689     onClick: function(e, el)
35690     {
35691         var time = this.endTimer - this.startTimer;
35692         // Roo.log(e.preventDefault());
35693         if(Roo.isTouch){
35694             if(time > 1000){
35695                 e.preventDefault();
35696                 return;
35697             }
35698         }
35699         
35700         if(!this.preventDefault){
35701             return;
35702         }
35703         
35704         e.preventDefault();
35705         
35706         if (this.activeClass != '') {
35707             this.selectBrick();
35708         }
35709         
35710         this.fireEvent('click', this, e);
35711     },
35712     
35713     enter: function(e, el)
35714     {
35715         e.preventDefault();
35716         
35717         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35718             return;
35719         }
35720         
35721         if(this.bgimage.length && this.html.length){
35722             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35723         }
35724     },
35725     
35726     leave: function(e, el)
35727     {
35728         e.preventDefault();
35729         
35730         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35731             return;
35732         }
35733         
35734         if(this.bgimage.length && this.html.length){
35735             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35736         }
35737     },
35738     
35739     onTouchStart: function(e, el)
35740     {
35741 //        e.preventDefault();
35742         
35743         this.touchmoved = false;
35744         
35745         if(!this.isFitContainer){
35746             return;
35747         }
35748         
35749         if(!this.bgimage.length || !this.html.length){
35750             return;
35751         }
35752         
35753         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35754         
35755         this.timer = new Date().getTime();
35756         
35757     },
35758     
35759     onTouchMove: function(e, el)
35760     {
35761         this.touchmoved = true;
35762     },
35763     
35764     onContextMenu : function(e,el)
35765     {
35766         e.preventDefault();
35767         e.stopPropagation();
35768         return false;
35769     },
35770     
35771     onTouchEnd: function(e, el)
35772     {
35773 //        e.preventDefault();
35774         
35775         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35776         
35777             this.leave(e,el);
35778             
35779             return;
35780         }
35781         
35782         if(!this.bgimage.length || !this.html.length){
35783             
35784             if(this.href.length){
35785                 window.location.href = this.href;
35786             }
35787             
35788             return;
35789         }
35790         
35791         if(!this.isFitContainer){
35792             return;
35793         }
35794         
35795         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35796         
35797         window.location.href = this.href;
35798     },
35799     
35800     //selection on single brick only
35801     selectBrick : function() {
35802         
35803         if (!this.parentId) {
35804             return;
35805         }
35806         
35807         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35808         var index = m.selectedBrick.indexOf(this.id);
35809         
35810         if ( index > -1) {
35811             m.selectedBrick.splice(index,1);
35812             this.el.removeClass(this.activeClass);
35813             return;
35814         }
35815         
35816         for(var i = 0; i < m.selectedBrick.length; i++) {
35817             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35818             b.el.removeClass(b.activeClass);
35819         }
35820         
35821         m.selectedBrick = [];
35822         
35823         m.selectedBrick.push(this.id);
35824         this.el.addClass(this.activeClass);
35825         return;
35826     },
35827     
35828     isSelected : function(){
35829         return this.el.hasClass(this.activeClass);
35830         
35831     }
35832 });
35833
35834 Roo.apply(Roo.bootstrap.MasonryBrick, {
35835     
35836     //groups: {},
35837     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35838      /**
35839     * register a Masonry Brick
35840     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35841     */
35842     
35843     register : function(brick)
35844     {
35845         //this.groups[brick.id] = brick;
35846         this.groups.add(brick.id, brick);
35847     },
35848     /**
35849     * fetch a  masonry brick based on the masonry brick ID
35850     * @param {string} the masonry brick to add
35851     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35852     */
35853     
35854     get: function(brick_id) 
35855     {
35856         // if (typeof(this.groups[brick_id]) == 'undefined') {
35857         //     return false;
35858         // }
35859         // return this.groups[brick_id] ;
35860         
35861         if(this.groups.key(brick_id)) {
35862             return this.groups.key(brick_id);
35863         }
35864         
35865         return false;
35866     }
35867     
35868     
35869     
35870 });
35871
35872  /*
35873  * - LGPL
35874  *
35875  * element
35876  * 
35877  */
35878
35879 /**
35880  * @class Roo.bootstrap.Brick
35881  * @extends Roo.bootstrap.Component
35882  * Bootstrap Brick class
35883  * 
35884  * @constructor
35885  * Create a new Brick
35886  * @param {Object} config The config object
35887  */
35888
35889 Roo.bootstrap.Brick = function(config){
35890     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35891     
35892     this.addEvents({
35893         // raw events
35894         /**
35895          * @event click
35896          * When a Brick is click
35897          * @param {Roo.bootstrap.Brick} this
35898          * @param {Roo.EventObject} e
35899          */
35900         "click" : true
35901     });
35902 };
35903
35904 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35905     
35906     /**
35907      * @cfg {String} title
35908      */   
35909     title : '',
35910     /**
35911      * @cfg {String} html
35912      */   
35913     html : '',
35914     /**
35915      * @cfg {String} bgimage
35916      */   
35917     bgimage : '',
35918     /**
35919      * @cfg {String} cls
35920      */   
35921     cls : '',
35922     /**
35923      * @cfg {String} href
35924      */   
35925     href : '',
35926     /**
35927      * @cfg {String} video
35928      */   
35929     video : '',
35930     /**
35931      * @cfg {Boolean} square
35932      */   
35933     square : true,
35934     
35935     getAutoCreate : function()
35936     {
35937         var cls = 'roo-brick';
35938         
35939         if(this.href.length){
35940             cls += ' roo-brick-link';
35941         }
35942         
35943         if(this.bgimage.length){
35944             cls += ' roo-brick-image';
35945         }
35946         
35947         if(!this.html.length && !this.bgimage.length){
35948             cls += ' roo-brick-center-title';
35949         }
35950         
35951         if(!this.html.length && this.bgimage.length){
35952             cls += ' roo-brick-bottom-title';
35953         }
35954         
35955         if(this.cls){
35956             cls += ' ' + this.cls;
35957         }
35958         
35959         var cfg = {
35960             tag: (this.href.length) ? 'a' : 'div',
35961             cls: cls,
35962             cn: [
35963                 {
35964                     tag: 'div',
35965                     cls: 'roo-brick-paragraph',
35966                     cn: []
35967                 }
35968             ]
35969         };
35970         
35971         if(this.href.length){
35972             cfg.href = this.href;
35973         }
35974         
35975         var cn = cfg.cn[0].cn;
35976         
35977         if(this.title.length){
35978             cn.push({
35979                 tag: 'h4',
35980                 cls: 'roo-brick-title',
35981                 html: this.title
35982             });
35983         }
35984         
35985         if(this.html.length){
35986             cn.push({
35987                 tag: 'p',
35988                 cls: 'roo-brick-text',
35989                 html: this.html
35990             });
35991         } else {
35992             cn.cls += ' hide';
35993         }
35994         
35995         if(this.bgimage.length){
35996             cfg.cn.push({
35997                 tag: 'img',
35998                 cls: 'roo-brick-image-view',
35999                 src: this.bgimage
36000             });
36001         }
36002         
36003         return cfg;
36004     },
36005     
36006     initEvents: function() 
36007     {
36008         if(this.title.length || this.html.length){
36009             this.el.on('mouseenter'  ,this.enter, this);
36010             this.el.on('mouseleave', this.leave, this);
36011         }
36012         
36013         Roo.EventManager.onWindowResize(this.resize, this); 
36014         
36015         if(this.bgimage.length){
36016             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36017             this.imageEl.on('load', this.onImageLoad, this);
36018             return;
36019         }
36020         
36021         this.resize();
36022     },
36023     
36024     onImageLoad : function()
36025     {
36026         this.resize();
36027     },
36028     
36029     resize : function()
36030     {
36031         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36032         
36033         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36034         
36035         if(this.bgimage.length){
36036             var image = this.el.select('.roo-brick-image-view', true).first();
36037             
36038             image.setWidth(paragraph.getWidth());
36039             
36040             if(this.square){
36041                 image.setHeight(paragraph.getWidth());
36042             }
36043             
36044             this.el.setHeight(image.getHeight());
36045             paragraph.setHeight(image.getHeight());
36046             
36047         }
36048         
36049     },
36050     
36051     enter: function(e, el)
36052     {
36053         e.preventDefault();
36054         
36055         if(this.bgimage.length){
36056             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36057             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36058         }
36059     },
36060     
36061     leave: function(e, el)
36062     {
36063         e.preventDefault();
36064         
36065         if(this.bgimage.length){
36066             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36067             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36068         }
36069     }
36070     
36071 });
36072
36073  
36074
36075  /*
36076  * - LGPL
36077  *
36078  * Number field 
36079  */
36080
36081 /**
36082  * @class Roo.bootstrap.NumberField
36083  * @extends Roo.bootstrap.Input
36084  * Bootstrap NumberField class
36085  * 
36086  * 
36087  * 
36088  * 
36089  * @constructor
36090  * Create a new NumberField
36091  * @param {Object} config The config object
36092  */
36093
36094 Roo.bootstrap.NumberField = function(config){
36095     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36096 };
36097
36098 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36099     
36100     /**
36101      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36102      */
36103     allowDecimals : true,
36104     /**
36105      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36106      */
36107     decimalSeparator : ".",
36108     /**
36109      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36110      */
36111     decimalPrecision : 2,
36112     /**
36113      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36114      */
36115     allowNegative : true,
36116     
36117     /**
36118      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36119      */
36120     allowZero: true,
36121     /**
36122      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36123      */
36124     minValue : Number.NEGATIVE_INFINITY,
36125     /**
36126      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36127      */
36128     maxValue : Number.MAX_VALUE,
36129     /**
36130      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36131      */
36132     minText : "The minimum value for this field is {0}",
36133     /**
36134      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36135      */
36136     maxText : "The maximum value for this field is {0}",
36137     /**
36138      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36139      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36140      */
36141     nanText : "{0} is not a valid number",
36142     /**
36143      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36144      */
36145     thousandsDelimiter : false,
36146     /**
36147      * @cfg {String} valueAlign alignment of value
36148      */
36149     valueAlign : "left",
36150
36151     getAutoCreate : function()
36152     {
36153         var hiddenInput = {
36154             tag: 'input',
36155             type: 'hidden',
36156             id: Roo.id(),
36157             cls: 'hidden-number-input'
36158         };
36159         
36160         if (this.name) {
36161             hiddenInput.name = this.name;
36162         }
36163         
36164         this.name = '';
36165         
36166         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36167         
36168         this.name = hiddenInput.name;
36169         
36170         if(cfg.cn.length > 0) {
36171             cfg.cn.push(hiddenInput);
36172         }
36173         
36174         return cfg;
36175     },
36176
36177     // private
36178     initEvents : function()
36179     {   
36180         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36181         
36182         var allowed = "0123456789";
36183         
36184         if(this.allowDecimals){
36185             allowed += this.decimalSeparator;
36186         }
36187         
36188         if(this.allowNegative){
36189             allowed += "-";
36190         }
36191         
36192         if(this.thousandsDelimiter) {
36193             allowed += ",";
36194         }
36195         
36196         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36197         
36198         var keyPress = function(e){
36199             
36200             var k = e.getKey();
36201             
36202             var c = e.getCharCode();
36203             
36204             if(
36205                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36206                     allowed.indexOf(String.fromCharCode(c)) === -1
36207             ){
36208                 e.stopEvent();
36209                 return;
36210             }
36211             
36212             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36213                 return;
36214             }
36215             
36216             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36217                 e.stopEvent();
36218             }
36219         };
36220         
36221         this.el.on("keypress", keyPress, this);
36222     },
36223     
36224     validateValue : function(value)
36225     {
36226         
36227         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36228             return false;
36229         }
36230         
36231         var num = this.parseValue(value);
36232         
36233         if(isNaN(num)){
36234             this.markInvalid(String.format(this.nanText, value));
36235             return false;
36236         }
36237         
36238         if(num < this.minValue){
36239             this.markInvalid(String.format(this.minText, this.minValue));
36240             return false;
36241         }
36242         
36243         if(num > this.maxValue){
36244             this.markInvalid(String.format(this.maxText, this.maxValue));
36245             return false;
36246         }
36247         
36248         return true;
36249     },
36250
36251     getValue : function()
36252     {
36253         var v = this.hiddenEl().getValue();
36254         
36255         return this.fixPrecision(this.parseValue(v));
36256     },
36257
36258     parseValue : function(value)
36259     {
36260         if(this.thousandsDelimiter) {
36261             value += "";
36262             r = new RegExp(",", "g");
36263             value = value.replace(r, "");
36264         }
36265         
36266         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36267         return isNaN(value) ? '' : value;
36268     },
36269
36270     fixPrecision : function(value)
36271     {
36272         if(this.thousandsDelimiter) {
36273             value += "";
36274             r = new RegExp(",", "g");
36275             value = value.replace(r, "");
36276         }
36277         
36278         var nan = isNaN(value);
36279         
36280         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36281             return nan ? '' : value;
36282         }
36283         return parseFloat(value).toFixed(this.decimalPrecision);
36284     },
36285
36286     setValue : function(v)
36287     {
36288         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36289         
36290         this.value = v;
36291         
36292         if(this.rendered){
36293             
36294             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36295             
36296             this.inputEl().dom.value = (v == '') ? '' :
36297                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36298             
36299             if(!this.allowZero && v === '0') {
36300                 this.hiddenEl().dom.value = '';
36301                 this.inputEl().dom.value = '';
36302             }
36303             
36304             this.validate();
36305         }
36306     },
36307
36308     decimalPrecisionFcn : function(v)
36309     {
36310         return Math.floor(v);
36311     },
36312
36313     beforeBlur : function()
36314     {
36315         var v = this.parseValue(this.getRawValue());
36316         
36317         if(v || v === 0 || v === ''){
36318             this.setValue(v);
36319         }
36320     },
36321     
36322     hiddenEl : function()
36323     {
36324         return this.el.select('input.hidden-number-input',true).first();
36325     }
36326     
36327 });
36328
36329  
36330
36331 /*
36332 * Licence: LGPL
36333 */
36334
36335 /**
36336  * @class Roo.bootstrap.DocumentSlider
36337  * @extends Roo.bootstrap.Component
36338  * Bootstrap DocumentSlider class
36339  * 
36340  * @constructor
36341  * Create a new DocumentViewer
36342  * @param {Object} config The config object
36343  */
36344
36345 Roo.bootstrap.DocumentSlider = function(config){
36346     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36347     
36348     this.files = [];
36349     
36350     this.addEvents({
36351         /**
36352          * @event initial
36353          * Fire after initEvent
36354          * @param {Roo.bootstrap.DocumentSlider} this
36355          */
36356         "initial" : true,
36357         /**
36358          * @event update
36359          * Fire after update
36360          * @param {Roo.bootstrap.DocumentSlider} this
36361          */
36362         "update" : true,
36363         /**
36364          * @event click
36365          * Fire after click
36366          * @param {Roo.bootstrap.DocumentSlider} this
36367          */
36368         "click" : true
36369     });
36370 };
36371
36372 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36373     
36374     files : false,
36375     
36376     indicator : 0,
36377     
36378     getAutoCreate : function()
36379     {
36380         var cfg = {
36381             tag : 'div',
36382             cls : 'roo-document-slider',
36383             cn : [
36384                 {
36385                     tag : 'div',
36386                     cls : 'roo-document-slider-header',
36387                     cn : [
36388                         {
36389                             tag : 'div',
36390                             cls : 'roo-document-slider-header-title'
36391                         }
36392                     ]
36393                 },
36394                 {
36395                     tag : 'div',
36396                     cls : 'roo-document-slider-body',
36397                     cn : [
36398                         {
36399                             tag : 'div',
36400                             cls : 'roo-document-slider-prev',
36401                             cn : [
36402                                 {
36403                                     tag : 'i',
36404                                     cls : 'fa fa-chevron-left'
36405                                 }
36406                             ]
36407                         },
36408                         {
36409                             tag : 'div',
36410                             cls : 'roo-document-slider-thumb',
36411                             cn : [
36412                                 {
36413                                     tag : 'img',
36414                                     cls : 'roo-document-slider-image'
36415                                 }
36416                             ]
36417                         },
36418                         {
36419                             tag : 'div',
36420                             cls : 'roo-document-slider-next',
36421                             cn : [
36422                                 {
36423                                     tag : 'i',
36424                                     cls : 'fa fa-chevron-right'
36425                                 }
36426                             ]
36427                         }
36428                     ]
36429                 }
36430             ]
36431         };
36432         
36433         return cfg;
36434     },
36435     
36436     initEvents : function()
36437     {
36438         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36439         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36440         
36441         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36442         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36443         
36444         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36445         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36446         
36447         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36448         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36449         
36450         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36451         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36452         
36453         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36454         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36455         
36456         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36457         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36458         
36459         this.thumbEl.on('click', this.onClick, this);
36460         
36461         this.prevIndicator.on('click', this.prev, this);
36462         
36463         this.nextIndicator.on('click', this.next, this);
36464         
36465     },
36466     
36467     initial : function()
36468     {
36469         if(this.files.length){
36470             this.indicator = 1;
36471             this.update()
36472         }
36473         
36474         this.fireEvent('initial', this);
36475     },
36476     
36477     update : function()
36478     {
36479         this.imageEl.attr('src', this.files[this.indicator - 1]);
36480         
36481         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36482         
36483         this.prevIndicator.show();
36484         
36485         if(this.indicator == 1){
36486             this.prevIndicator.hide();
36487         }
36488         
36489         this.nextIndicator.show();
36490         
36491         if(this.indicator == this.files.length){
36492             this.nextIndicator.hide();
36493         }
36494         
36495         this.thumbEl.scrollTo('top');
36496         
36497         this.fireEvent('update', this);
36498     },
36499     
36500     onClick : function(e)
36501     {
36502         e.preventDefault();
36503         
36504         this.fireEvent('click', this);
36505     },
36506     
36507     prev : function(e)
36508     {
36509         e.preventDefault();
36510         
36511         this.indicator = Math.max(1, this.indicator - 1);
36512         
36513         this.update();
36514     },
36515     
36516     next : function(e)
36517     {
36518         e.preventDefault();
36519         
36520         this.indicator = Math.min(this.files.length, this.indicator + 1);
36521         
36522         this.update();
36523     }
36524 });
36525 /*
36526  * - LGPL
36527  *
36528  * RadioSet
36529  *
36530  *
36531  */
36532
36533 /**
36534  * @class Roo.bootstrap.RadioSet
36535  * @extends Roo.bootstrap.Input
36536  * Bootstrap RadioSet class
36537  * @cfg {String} indicatorpos (left|right) default left
36538  * @cfg {Boolean} inline (true|false) inline the element (default true)
36539  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36540  * @constructor
36541  * Create a new RadioSet
36542  * @param {Object} config The config object
36543  */
36544
36545 Roo.bootstrap.RadioSet = function(config){
36546     
36547     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36548     
36549     this.radioes = [];
36550     
36551     Roo.bootstrap.RadioSet.register(this);
36552     
36553     this.addEvents({
36554         /**
36555         * @event check
36556         * Fires when the element is checked or unchecked.
36557         * @param {Roo.bootstrap.RadioSet} this This radio
36558         * @param {Roo.bootstrap.Radio} item The checked item
36559         */
36560        check : true,
36561        /**
36562         * @event click
36563         * Fires when the element is click.
36564         * @param {Roo.bootstrap.RadioSet} this This radio set
36565         * @param {Roo.bootstrap.Radio} item The checked item
36566         * @param {Roo.EventObject} e The event object
36567         */
36568        click : true
36569     });
36570     
36571 };
36572
36573 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36574
36575     radioes : false,
36576     
36577     inline : true,
36578     
36579     weight : '',
36580     
36581     indicatorpos : 'left',
36582     
36583     getAutoCreate : function()
36584     {
36585         var label = {
36586             tag : 'label',
36587             cls : 'roo-radio-set-label',
36588             cn : [
36589                 {
36590                     tag : 'span',
36591                     html : this.fieldLabel
36592                 }
36593             ]
36594         };
36595         if (Roo.bootstrap.version == 3) {
36596             
36597             
36598             if(this.indicatorpos == 'left'){
36599                 label.cn.unshift({
36600                     tag : 'i',
36601                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36602                     tooltip : 'This field is required'
36603                 });
36604             } else {
36605                 label.cn.push({
36606                     tag : 'i',
36607                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36608                     tooltip : 'This field is required'
36609                 });
36610             }
36611         }
36612         var items = {
36613             tag : 'div',
36614             cls : 'roo-radio-set-items'
36615         };
36616         
36617         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36618         
36619         if (align === 'left' && this.fieldLabel.length) {
36620             
36621             items = {
36622                 cls : "roo-radio-set-right", 
36623                 cn: [
36624                     items
36625                 ]
36626             };
36627             
36628             if(this.labelWidth > 12){
36629                 label.style = "width: " + this.labelWidth + 'px';
36630             }
36631             
36632             if(this.labelWidth < 13 && this.labelmd == 0){
36633                 this.labelmd = this.labelWidth;
36634             }
36635             
36636             if(this.labellg > 0){
36637                 label.cls += ' col-lg-' + this.labellg;
36638                 items.cls += ' col-lg-' + (12 - this.labellg);
36639             }
36640             
36641             if(this.labelmd > 0){
36642                 label.cls += ' col-md-' + this.labelmd;
36643                 items.cls += ' col-md-' + (12 - this.labelmd);
36644             }
36645             
36646             if(this.labelsm > 0){
36647                 label.cls += ' col-sm-' + this.labelsm;
36648                 items.cls += ' col-sm-' + (12 - this.labelsm);
36649             }
36650             
36651             if(this.labelxs > 0){
36652                 label.cls += ' col-xs-' + this.labelxs;
36653                 items.cls += ' col-xs-' + (12 - this.labelxs);
36654             }
36655         }
36656         
36657         var cfg = {
36658             tag : 'div',
36659             cls : 'roo-radio-set',
36660             cn : [
36661                 {
36662                     tag : 'input',
36663                     cls : 'roo-radio-set-input',
36664                     type : 'hidden',
36665                     name : this.name,
36666                     value : this.value ? this.value :  ''
36667                 },
36668                 label,
36669                 items
36670             ]
36671         };
36672         
36673         if(this.weight.length){
36674             cfg.cls += ' roo-radio-' + this.weight;
36675         }
36676         
36677         if(this.inline) {
36678             cfg.cls += ' roo-radio-set-inline';
36679         }
36680         
36681         var settings=this;
36682         ['xs','sm','md','lg'].map(function(size){
36683             if (settings[size]) {
36684                 cfg.cls += ' col-' + size + '-' + settings[size];
36685             }
36686         });
36687         
36688         return cfg;
36689         
36690     },
36691
36692     initEvents : function()
36693     {
36694         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36695         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36696         
36697         if(!this.fieldLabel.length){
36698             this.labelEl.hide();
36699         }
36700         
36701         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36702         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36703         
36704         this.indicator = this.indicatorEl();
36705         
36706         if(this.indicator){
36707             this.indicator.addClass('invisible');
36708         }
36709         
36710         this.originalValue = this.getValue();
36711         
36712     },
36713     
36714     inputEl: function ()
36715     {
36716         return this.el.select('.roo-radio-set-input', true).first();
36717     },
36718     
36719     getChildContainer : function()
36720     {
36721         return this.itemsEl;
36722     },
36723     
36724     register : function(item)
36725     {
36726         this.radioes.push(item);
36727         
36728     },
36729     
36730     validate : function()
36731     {   
36732         if(this.getVisibilityEl().hasClass('hidden')){
36733             return true;
36734         }
36735         
36736         var valid = false;
36737         
36738         Roo.each(this.radioes, function(i){
36739             if(!i.checked){
36740                 return;
36741             }
36742             
36743             valid = true;
36744             return false;
36745         });
36746         
36747         if(this.allowBlank) {
36748             return true;
36749         }
36750         
36751         if(this.disabled || valid){
36752             this.markValid();
36753             return true;
36754         }
36755         
36756         this.markInvalid();
36757         return false;
36758         
36759     },
36760     
36761     markValid : function()
36762     {
36763         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36764             this.indicatorEl().removeClass('visible');
36765             this.indicatorEl().addClass('invisible');
36766         }
36767         
36768         
36769         if (Roo.bootstrap.version == 3) {
36770             this.el.removeClass([this.invalidClass, this.validClass]);
36771             this.el.addClass(this.validClass);
36772         } else {
36773             this.el.removeClass(['is-invalid','is-valid']);
36774             this.el.addClass(['is-valid']);
36775         }
36776         this.fireEvent('valid', this);
36777     },
36778     
36779     markInvalid : function(msg)
36780     {
36781         if(this.allowBlank || this.disabled){
36782             return;
36783         }
36784         
36785         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36786             this.indicatorEl().removeClass('invisible');
36787             this.indicatorEl().addClass('visible');
36788         }
36789         if (Roo.bootstrap.version == 3) {
36790             this.el.removeClass([this.invalidClass, this.validClass]);
36791             this.el.addClass(this.invalidClass);
36792         } else {
36793             this.el.removeClass(['is-invalid','is-valid']);
36794             this.el.addClass(['is-invalid']);
36795         }
36796         
36797         this.fireEvent('invalid', this, msg);
36798         
36799     },
36800     
36801     setValue : function(v, suppressEvent)
36802     {   
36803         if(this.value === v){
36804             return;
36805         }
36806         
36807         this.value = v;
36808         
36809         if(this.rendered){
36810             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36811         }
36812         
36813         Roo.each(this.radioes, function(i){
36814             i.checked = false;
36815             i.el.removeClass('checked');
36816         });
36817         
36818         Roo.each(this.radioes, function(i){
36819             
36820             if(i.value === v || i.value.toString() === v.toString()){
36821                 i.checked = true;
36822                 i.el.addClass('checked');
36823                 
36824                 if(suppressEvent !== true){
36825                     this.fireEvent('check', this, i);
36826                 }
36827                 
36828                 return false;
36829             }
36830             
36831         }, this);
36832         
36833         this.validate();
36834     },
36835     
36836     clearInvalid : function(){
36837         
36838         if(!this.el || this.preventMark){
36839             return;
36840         }
36841         
36842         this.el.removeClass([this.invalidClass]);
36843         
36844         this.fireEvent('valid', this);
36845     }
36846     
36847 });
36848
36849 Roo.apply(Roo.bootstrap.RadioSet, {
36850     
36851     groups: {},
36852     
36853     register : function(set)
36854     {
36855         this.groups[set.name] = set;
36856     },
36857     
36858     get: function(name) 
36859     {
36860         if (typeof(this.groups[name]) == 'undefined') {
36861             return false;
36862         }
36863         
36864         return this.groups[name] ;
36865     }
36866     
36867 });
36868 /*
36869  * Based on:
36870  * Ext JS Library 1.1.1
36871  * Copyright(c) 2006-2007, Ext JS, LLC.
36872  *
36873  * Originally Released Under LGPL - original licence link has changed is not relivant.
36874  *
36875  * Fork - LGPL
36876  * <script type="text/javascript">
36877  */
36878
36879
36880 /**
36881  * @class Roo.bootstrap.SplitBar
36882  * @extends Roo.util.Observable
36883  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36884  * <br><br>
36885  * Usage:
36886  * <pre><code>
36887 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36888                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36889 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36890 split.minSize = 100;
36891 split.maxSize = 600;
36892 split.animate = true;
36893 split.on('moved', splitterMoved);
36894 </code></pre>
36895  * @constructor
36896  * Create a new SplitBar
36897  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36898  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36899  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36900  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36901                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36902                         position of the SplitBar).
36903  */
36904 Roo.bootstrap.SplitBar = function(cfg){
36905     
36906     /** @private */
36907     
36908     //{
36909     //  dragElement : elm
36910     //  resizingElement: el,
36911         // optional..
36912     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36913     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36914         // existingProxy ???
36915     //}
36916     
36917     this.el = Roo.get(cfg.dragElement, true);
36918     this.el.dom.unselectable = "on";
36919     /** @private */
36920     this.resizingEl = Roo.get(cfg.resizingElement, true);
36921
36922     /**
36923      * @private
36924      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36925      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36926      * @type Number
36927      */
36928     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36929     
36930     /**
36931      * The minimum size of the resizing element. (Defaults to 0)
36932      * @type Number
36933      */
36934     this.minSize = 0;
36935     
36936     /**
36937      * The maximum size of the resizing element. (Defaults to 2000)
36938      * @type Number
36939      */
36940     this.maxSize = 2000;
36941     
36942     /**
36943      * Whether to animate the transition to the new size
36944      * @type Boolean
36945      */
36946     this.animate = false;
36947     
36948     /**
36949      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36950      * @type Boolean
36951      */
36952     this.useShim = false;
36953     
36954     /** @private */
36955     this.shim = null;
36956     
36957     if(!cfg.existingProxy){
36958         /** @private */
36959         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36960     }else{
36961         this.proxy = Roo.get(cfg.existingProxy).dom;
36962     }
36963     /** @private */
36964     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36965     
36966     /** @private */
36967     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36968     
36969     /** @private */
36970     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36971     
36972     /** @private */
36973     this.dragSpecs = {};
36974     
36975     /**
36976      * @private The adapter to use to positon and resize elements
36977      */
36978     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36979     this.adapter.init(this);
36980     
36981     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36982         /** @private */
36983         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36984         this.el.addClass("roo-splitbar-h");
36985     }else{
36986         /** @private */
36987         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36988         this.el.addClass("roo-splitbar-v");
36989     }
36990     
36991     this.addEvents({
36992         /**
36993          * @event resize
36994          * Fires when the splitter is moved (alias for {@link #event-moved})
36995          * @param {Roo.bootstrap.SplitBar} this
36996          * @param {Number} newSize the new width or height
36997          */
36998         "resize" : true,
36999         /**
37000          * @event moved
37001          * Fires when the splitter is moved
37002          * @param {Roo.bootstrap.SplitBar} this
37003          * @param {Number} newSize the new width or height
37004          */
37005         "moved" : true,
37006         /**
37007          * @event beforeresize
37008          * Fires before the splitter is dragged
37009          * @param {Roo.bootstrap.SplitBar} this
37010          */
37011         "beforeresize" : true,
37012
37013         "beforeapply" : true
37014     });
37015
37016     Roo.util.Observable.call(this);
37017 };
37018
37019 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37020     onStartProxyDrag : function(x, y){
37021         this.fireEvent("beforeresize", this);
37022         if(!this.overlay){
37023             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37024             o.unselectable();
37025             o.enableDisplayMode("block");
37026             // all splitbars share the same overlay
37027             Roo.bootstrap.SplitBar.prototype.overlay = o;
37028         }
37029         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37030         this.overlay.show();
37031         Roo.get(this.proxy).setDisplayed("block");
37032         var size = this.adapter.getElementSize(this);
37033         this.activeMinSize = this.getMinimumSize();;
37034         this.activeMaxSize = this.getMaximumSize();;
37035         var c1 = size - this.activeMinSize;
37036         var c2 = Math.max(this.activeMaxSize - size, 0);
37037         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37038             this.dd.resetConstraints();
37039             this.dd.setXConstraint(
37040                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37041                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37042             );
37043             this.dd.setYConstraint(0, 0);
37044         }else{
37045             this.dd.resetConstraints();
37046             this.dd.setXConstraint(0, 0);
37047             this.dd.setYConstraint(
37048                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37049                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37050             );
37051          }
37052         this.dragSpecs.startSize = size;
37053         this.dragSpecs.startPoint = [x, y];
37054         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37055     },
37056     
37057     /** 
37058      * @private Called after the drag operation by the DDProxy
37059      */
37060     onEndProxyDrag : function(e){
37061         Roo.get(this.proxy).setDisplayed(false);
37062         var endPoint = Roo.lib.Event.getXY(e);
37063         if(this.overlay){
37064             this.overlay.hide();
37065         }
37066         var newSize;
37067         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37068             newSize = this.dragSpecs.startSize + 
37069                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37070                     endPoint[0] - this.dragSpecs.startPoint[0] :
37071                     this.dragSpecs.startPoint[0] - endPoint[0]
37072                 );
37073         }else{
37074             newSize = this.dragSpecs.startSize + 
37075                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37076                     endPoint[1] - this.dragSpecs.startPoint[1] :
37077                     this.dragSpecs.startPoint[1] - endPoint[1]
37078                 );
37079         }
37080         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37081         if(newSize != this.dragSpecs.startSize){
37082             if(this.fireEvent('beforeapply', this, newSize) !== false){
37083                 this.adapter.setElementSize(this, newSize);
37084                 this.fireEvent("moved", this, newSize);
37085                 this.fireEvent("resize", this, newSize);
37086             }
37087         }
37088     },
37089     
37090     /**
37091      * Get the adapter this SplitBar uses
37092      * @return The adapter object
37093      */
37094     getAdapter : function(){
37095         return this.adapter;
37096     },
37097     
37098     /**
37099      * Set the adapter this SplitBar uses
37100      * @param {Object} adapter A SplitBar adapter object
37101      */
37102     setAdapter : function(adapter){
37103         this.adapter = adapter;
37104         this.adapter.init(this);
37105     },
37106     
37107     /**
37108      * Gets the minimum size for the resizing element
37109      * @return {Number} The minimum size
37110      */
37111     getMinimumSize : function(){
37112         return this.minSize;
37113     },
37114     
37115     /**
37116      * Sets the minimum size for the resizing element
37117      * @param {Number} minSize The minimum size
37118      */
37119     setMinimumSize : function(minSize){
37120         this.minSize = minSize;
37121     },
37122     
37123     /**
37124      * Gets the maximum size for the resizing element
37125      * @return {Number} The maximum size
37126      */
37127     getMaximumSize : function(){
37128         return this.maxSize;
37129     },
37130     
37131     /**
37132      * Sets the maximum size for the resizing element
37133      * @param {Number} maxSize The maximum size
37134      */
37135     setMaximumSize : function(maxSize){
37136         this.maxSize = maxSize;
37137     },
37138     
37139     /**
37140      * Sets the initialize size for the resizing element
37141      * @param {Number} size The initial size
37142      */
37143     setCurrentSize : function(size){
37144         var oldAnimate = this.animate;
37145         this.animate = false;
37146         this.adapter.setElementSize(this, size);
37147         this.animate = oldAnimate;
37148     },
37149     
37150     /**
37151      * Destroy this splitbar. 
37152      * @param {Boolean} removeEl True to remove the element
37153      */
37154     destroy : function(removeEl){
37155         if(this.shim){
37156             this.shim.remove();
37157         }
37158         this.dd.unreg();
37159         this.proxy.parentNode.removeChild(this.proxy);
37160         if(removeEl){
37161             this.el.remove();
37162         }
37163     }
37164 });
37165
37166 /**
37167  * @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.
37168  */
37169 Roo.bootstrap.SplitBar.createProxy = function(dir){
37170     var proxy = new Roo.Element(document.createElement("div"));
37171     proxy.unselectable();
37172     var cls = 'roo-splitbar-proxy';
37173     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37174     document.body.appendChild(proxy.dom);
37175     return proxy.dom;
37176 };
37177
37178 /** 
37179  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37180  * Default Adapter. It assumes the splitter and resizing element are not positioned
37181  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37182  */
37183 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37184 };
37185
37186 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37187     // do nothing for now
37188     init : function(s){
37189     
37190     },
37191     /**
37192      * Called before drag operations to get the current size of the resizing element. 
37193      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37194      */
37195      getElementSize : function(s){
37196         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37197             return s.resizingEl.getWidth();
37198         }else{
37199             return s.resizingEl.getHeight();
37200         }
37201     },
37202     
37203     /**
37204      * Called after drag operations to set the size of the resizing element.
37205      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37206      * @param {Number} newSize The new size to set
37207      * @param {Function} onComplete A function to be invoked when resizing is complete
37208      */
37209     setElementSize : function(s, newSize, onComplete){
37210         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37211             if(!s.animate){
37212                 s.resizingEl.setWidth(newSize);
37213                 if(onComplete){
37214                     onComplete(s, newSize);
37215                 }
37216             }else{
37217                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37218             }
37219         }else{
37220             
37221             if(!s.animate){
37222                 s.resizingEl.setHeight(newSize);
37223                 if(onComplete){
37224                     onComplete(s, newSize);
37225                 }
37226             }else{
37227                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37228             }
37229         }
37230     }
37231 };
37232
37233 /** 
37234  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37235  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37236  * Adapter that  moves the splitter element to align with the resized sizing element. 
37237  * Used with an absolute positioned SplitBar.
37238  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37239  * document.body, make sure you assign an id to the body element.
37240  */
37241 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37242     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37243     this.container = Roo.get(container);
37244 };
37245
37246 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37247     init : function(s){
37248         this.basic.init(s);
37249     },
37250     
37251     getElementSize : function(s){
37252         return this.basic.getElementSize(s);
37253     },
37254     
37255     setElementSize : function(s, newSize, onComplete){
37256         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37257     },
37258     
37259     moveSplitter : function(s){
37260         var yes = Roo.bootstrap.SplitBar;
37261         switch(s.placement){
37262             case yes.LEFT:
37263                 s.el.setX(s.resizingEl.getRight());
37264                 break;
37265             case yes.RIGHT:
37266                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37267                 break;
37268             case yes.TOP:
37269                 s.el.setY(s.resizingEl.getBottom());
37270                 break;
37271             case yes.BOTTOM:
37272                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37273                 break;
37274         }
37275     }
37276 };
37277
37278 /**
37279  * Orientation constant - Create a vertical SplitBar
37280  * @static
37281  * @type Number
37282  */
37283 Roo.bootstrap.SplitBar.VERTICAL = 1;
37284
37285 /**
37286  * Orientation constant - Create a horizontal SplitBar
37287  * @static
37288  * @type Number
37289  */
37290 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37291
37292 /**
37293  * Placement constant - The resizing element is to the left of the splitter element
37294  * @static
37295  * @type Number
37296  */
37297 Roo.bootstrap.SplitBar.LEFT = 1;
37298
37299 /**
37300  * Placement constant - The resizing element is to the right of the splitter element
37301  * @static
37302  * @type Number
37303  */
37304 Roo.bootstrap.SplitBar.RIGHT = 2;
37305
37306 /**
37307  * Placement constant - The resizing element is positioned above the splitter element
37308  * @static
37309  * @type Number
37310  */
37311 Roo.bootstrap.SplitBar.TOP = 3;
37312
37313 /**
37314  * Placement constant - The resizing element is positioned under splitter element
37315  * @static
37316  * @type Number
37317  */
37318 Roo.bootstrap.SplitBar.BOTTOM = 4;
37319 Roo.namespace("Roo.bootstrap.layout");/*
37320  * Based on:
37321  * Ext JS Library 1.1.1
37322  * Copyright(c) 2006-2007, Ext JS, LLC.
37323  *
37324  * Originally Released Under LGPL - original licence link has changed is not relivant.
37325  *
37326  * Fork - LGPL
37327  * <script type="text/javascript">
37328  */
37329
37330 /**
37331  * @class Roo.bootstrap.layout.Manager
37332  * @extends Roo.bootstrap.Component
37333  * Base class for layout managers.
37334  */
37335 Roo.bootstrap.layout.Manager = function(config)
37336 {
37337     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37338
37339
37340
37341
37342
37343     /** false to disable window resize monitoring @type Boolean */
37344     this.monitorWindowResize = true;
37345     this.regions = {};
37346     this.addEvents({
37347         /**
37348          * @event layout
37349          * Fires when a layout is performed.
37350          * @param {Roo.LayoutManager} this
37351          */
37352         "layout" : true,
37353         /**
37354          * @event regionresized
37355          * Fires when the user resizes a region.
37356          * @param {Roo.LayoutRegion} region The resized region
37357          * @param {Number} newSize The new size (width for east/west, height for north/south)
37358          */
37359         "regionresized" : true,
37360         /**
37361          * @event regioncollapsed
37362          * Fires when a region is collapsed.
37363          * @param {Roo.LayoutRegion} region The collapsed region
37364          */
37365         "regioncollapsed" : true,
37366         /**
37367          * @event regionexpanded
37368          * Fires when a region is expanded.
37369          * @param {Roo.LayoutRegion} region The expanded region
37370          */
37371         "regionexpanded" : true
37372     });
37373     this.updating = false;
37374
37375     if (config.el) {
37376         this.el = Roo.get(config.el);
37377         this.initEvents();
37378     }
37379
37380 };
37381
37382 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37383
37384
37385     regions : null,
37386
37387     monitorWindowResize : true,
37388
37389
37390     updating : false,
37391
37392
37393     onRender : function(ct, position)
37394     {
37395         if(!this.el){
37396             this.el = Roo.get(ct);
37397             this.initEvents();
37398         }
37399         //this.fireEvent('render',this);
37400     },
37401
37402
37403     initEvents: function()
37404     {
37405
37406
37407         // ie scrollbar fix
37408         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37409             document.body.scroll = "no";
37410         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37411             this.el.position('relative');
37412         }
37413         this.id = this.el.id;
37414         this.el.addClass("roo-layout-container");
37415         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37416         if(this.el.dom != document.body ) {
37417             this.el.on('resize', this.layout,this);
37418             this.el.on('show', this.layout,this);
37419         }
37420
37421     },
37422
37423     /**
37424      * Returns true if this layout is currently being updated
37425      * @return {Boolean}
37426      */
37427     isUpdating : function(){
37428         return this.updating;
37429     },
37430
37431     /**
37432      * Suspend the LayoutManager from doing auto-layouts while
37433      * making multiple add or remove calls
37434      */
37435     beginUpdate : function(){
37436         this.updating = true;
37437     },
37438
37439     /**
37440      * Restore auto-layouts and optionally disable the manager from performing a layout
37441      * @param {Boolean} noLayout true to disable a layout update
37442      */
37443     endUpdate : function(noLayout){
37444         this.updating = false;
37445         if(!noLayout){
37446             this.layout();
37447         }
37448     },
37449
37450     layout: function(){
37451         // abstract...
37452     },
37453
37454     onRegionResized : function(region, newSize){
37455         this.fireEvent("regionresized", region, newSize);
37456         this.layout();
37457     },
37458
37459     onRegionCollapsed : function(region){
37460         this.fireEvent("regioncollapsed", region);
37461     },
37462
37463     onRegionExpanded : function(region){
37464         this.fireEvent("regionexpanded", region);
37465     },
37466
37467     /**
37468      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37469      * performs box-model adjustments.
37470      * @return {Object} The size as an object {width: (the width), height: (the height)}
37471      */
37472     getViewSize : function()
37473     {
37474         var size;
37475         if(this.el.dom != document.body){
37476             size = this.el.getSize();
37477         }else{
37478             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37479         }
37480         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37481         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37482         return size;
37483     },
37484
37485     /**
37486      * Returns the Element this layout is bound to.
37487      * @return {Roo.Element}
37488      */
37489     getEl : function(){
37490         return this.el;
37491     },
37492
37493     /**
37494      * Returns the specified region.
37495      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37496      * @return {Roo.LayoutRegion}
37497      */
37498     getRegion : function(target){
37499         return this.regions[target.toLowerCase()];
37500     },
37501
37502     onWindowResize : function(){
37503         if(this.monitorWindowResize){
37504             this.layout();
37505         }
37506     }
37507 });
37508 /*
37509  * Based on:
37510  * Ext JS Library 1.1.1
37511  * Copyright(c) 2006-2007, Ext JS, LLC.
37512  *
37513  * Originally Released Under LGPL - original licence link has changed is not relivant.
37514  *
37515  * Fork - LGPL
37516  * <script type="text/javascript">
37517  */
37518 /**
37519  * @class Roo.bootstrap.layout.Border
37520  * @extends Roo.bootstrap.layout.Manager
37521  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37522  * please see: examples/bootstrap/nested.html<br><br>
37523  
37524 <b>The container the layout is rendered into can be either the body element or any other element.
37525 If it is not the body element, the container needs to either be an absolute positioned element,
37526 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37527 the container size if it is not the body element.</b>
37528
37529 * @constructor
37530 * Create a new Border
37531 * @param {Object} config Configuration options
37532  */
37533 Roo.bootstrap.layout.Border = function(config){
37534     config = config || {};
37535     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37536     
37537     
37538     
37539     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37540         if(config[region]){
37541             config[region].region = region;
37542             this.addRegion(config[region]);
37543         }
37544     },this);
37545     
37546 };
37547
37548 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37549
37550 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37551     
37552     parent : false, // this might point to a 'nest' or a ???
37553     
37554     /**
37555      * Creates and adds a new region if it doesn't already exist.
37556      * @param {String} target The target region key (north, south, east, west or center).
37557      * @param {Object} config The regions config object
37558      * @return {BorderLayoutRegion} The new region
37559      */
37560     addRegion : function(config)
37561     {
37562         if(!this.regions[config.region]){
37563             var r = this.factory(config);
37564             this.bindRegion(r);
37565         }
37566         return this.regions[config.region];
37567     },
37568
37569     // private (kinda)
37570     bindRegion : function(r){
37571         this.regions[r.config.region] = r;
37572         
37573         r.on("visibilitychange",    this.layout, this);
37574         r.on("paneladded",          this.layout, this);
37575         r.on("panelremoved",        this.layout, this);
37576         r.on("invalidated",         this.layout, this);
37577         r.on("resized",             this.onRegionResized, this);
37578         r.on("collapsed",           this.onRegionCollapsed, this);
37579         r.on("expanded",            this.onRegionExpanded, this);
37580     },
37581
37582     /**
37583      * Performs a layout update.
37584      */
37585     layout : function()
37586     {
37587         if(this.updating) {
37588             return;
37589         }
37590         
37591         // render all the rebions if they have not been done alreayd?
37592         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37593             if(this.regions[region] && !this.regions[region].bodyEl){
37594                 this.regions[region].onRender(this.el)
37595             }
37596         },this);
37597         
37598         var size = this.getViewSize();
37599         var w = size.width;
37600         var h = size.height;
37601         var centerW = w;
37602         var centerH = h;
37603         var centerY = 0;
37604         var centerX = 0;
37605         //var x = 0, y = 0;
37606
37607         var rs = this.regions;
37608         var north = rs["north"];
37609         var south = rs["south"]; 
37610         var west = rs["west"];
37611         var east = rs["east"];
37612         var center = rs["center"];
37613         //if(this.hideOnLayout){ // not supported anymore
37614             //c.el.setStyle("display", "none");
37615         //}
37616         if(north && north.isVisible()){
37617             var b = north.getBox();
37618             var m = north.getMargins();
37619             b.width = w - (m.left+m.right);
37620             b.x = m.left;
37621             b.y = m.top;
37622             centerY = b.height + b.y + m.bottom;
37623             centerH -= centerY;
37624             north.updateBox(this.safeBox(b));
37625         }
37626         if(south && south.isVisible()){
37627             var b = south.getBox();
37628             var m = south.getMargins();
37629             b.width = w - (m.left+m.right);
37630             b.x = m.left;
37631             var totalHeight = (b.height + m.top + m.bottom);
37632             b.y = h - totalHeight + m.top;
37633             centerH -= totalHeight;
37634             south.updateBox(this.safeBox(b));
37635         }
37636         if(west && west.isVisible()){
37637             var b = west.getBox();
37638             var m = west.getMargins();
37639             b.height = centerH - (m.top+m.bottom);
37640             b.x = m.left;
37641             b.y = centerY + m.top;
37642             var totalWidth = (b.width + m.left + m.right);
37643             centerX += totalWidth;
37644             centerW -= totalWidth;
37645             west.updateBox(this.safeBox(b));
37646         }
37647         if(east && east.isVisible()){
37648             var b = east.getBox();
37649             var m = east.getMargins();
37650             b.height = centerH - (m.top+m.bottom);
37651             var totalWidth = (b.width + m.left + m.right);
37652             b.x = w - totalWidth + m.left;
37653             b.y = centerY + m.top;
37654             centerW -= totalWidth;
37655             east.updateBox(this.safeBox(b));
37656         }
37657         if(center){
37658             var m = center.getMargins();
37659             var centerBox = {
37660                 x: centerX + m.left,
37661                 y: centerY + m.top,
37662                 width: centerW - (m.left+m.right),
37663                 height: centerH - (m.top+m.bottom)
37664             };
37665             //if(this.hideOnLayout){
37666                 //center.el.setStyle("display", "block");
37667             //}
37668             center.updateBox(this.safeBox(centerBox));
37669         }
37670         this.el.repaint();
37671         this.fireEvent("layout", this);
37672     },
37673
37674     // private
37675     safeBox : function(box){
37676         box.width = Math.max(0, box.width);
37677         box.height = Math.max(0, box.height);
37678         return box;
37679     },
37680
37681     /**
37682      * Adds a ContentPanel (or subclass) to this layout.
37683      * @param {String} target The target region key (north, south, east, west or center).
37684      * @param {Roo.ContentPanel} panel The panel to add
37685      * @return {Roo.ContentPanel} The added panel
37686      */
37687     add : function(target, panel){
37688          
37689         target = target.toLowerCase();
37690         return this.regions[target].add(panel);
37691     },
37692
37693     /**
37694      * Remove a ContentPanel (or subclass) to this layout.
37695      * @param {String} target The target region key (north, south, east, west or center).
37696      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37697      * @return {Roo.ContentPanel} The removed panel
37698      */
37699     remove : function(target, panel){
37700         target = target.toLowerCase();
37701         return this.regions[target].remove(panel);
37702     },
37703
37704     /**
37705      * Searches all regions for a panel with the specified id
37706      * @param {String} panelId
37707      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37708      */
37709     findPanel : function(panelId){
37710         var rs = this.regions;
37711         for(var target in rs){
37712             if(typeof rs[target] != "function"){
37713                 var p = rs[target].getPanel(panelId);
37714                 if(p){
37715                     return p;
37716                 }
37717             }
37718         }
37719         return null;
37720     },
37721
37722     /**
37723      * Searches all regions for a panel with the specified id and activates (shows) it.
37724      * @param {String/ContentPanel} panelId The panels id or the panel itself
37725      * @return {Roo.ContentPanel} The shown panel or null
37726      */
37727     showPanel : function(panelId) {
37728       var rs = this.regions;
37729       for(var target in rs){
37730          var r = rs[target];
37731          if(typeof r != "function"){
37732             if(r.hasPanel(panelId)){
37733                return r.showPanel(panelId);
37734             }
37735          }
37736       }
37737       return null;
37738    },
37739
37740    /**
37741      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37742      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37743      */
37744    /*
37745     restoreState : function(provider){
37746         if(!provider){
37747             provider = Roo.state.Manager;
37748         }
37749         var sm = new Roo.LayoutStateManager();
37750         sm.init(this, provider);
37751     },
37752 */
37753  
37754  
37755     /**
37756      * Adds a xtype elements to the layout.
37757      * <pre><code>
37758
37759 layout.addxtype({
37760        xtype : 'ContentPanel',
37761        region: 'west',
37762        items: [ .... ]
37763    }
37764 );
37765
37766 layout.addxtype({
37767         xtype : 'NestedLayoutPanel',
37768         region: 'west',
37769         layout: {
37770            center: { },
37771            west: { }   
37772         },
37773         items : [ ... list of content panels or nested layout panels.. ]
37774    }
37775 );
37776 </code></pre>
37777      * @param {Object} cfg Xtype definition of item to add.
37778      */
37779     addxtype : function(cfg)
37780     {
37781         // basically accepts a pannel...
37782         // can accept a layout region..!?!?
37783         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37784         
37785         
37786         // theory?  children can only be panels??
37787         
37788         //if (!cfg.xtype.match(/Panel$/)) {
37789         //    return false;
37790         //}
37791         var ret = false;
37792         
37793         if (typeof(cfg.region) == 'undefined') {
37794             Roo.log("Failed to add Panel, region was not set");
37795             Roo.log(cfg);
37796             return false;
37797         }
37798         var region = cfg.region;
37799         delete cfg.region;
37800         
37801           
37802         var xitems = [];
37803         if (cfg.items) {
37804             xitems = cfg.items;
37805             delete cfg.items;
37806         }
37807         var nb = false;
37808         
37809         if ( region == 'center') {
37810             Roo.log("Center: " + cfg.title);
37811         }
37812         
37813         
37814         switch(cfg.xtype) 
37815         {
37816             case 'Content':  // ContentPanel (el, cfg)
37817             case 'Scroll':  // ContentPanel (el, cfg)
37818             case 'View': 
37819                 cfg.autoCreate = cfg.autoCreate || true;
37820                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37821                 //} else {
37822                 //    var el = this.el.createChild();
37823                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37824                 //}
37825                 
37826                 this.add(region, ret);
37827                 break;
37828             
37829             /*
37830             case 'TreePanel': // our new panel!
37831                 cfg.el = this.el.createChild();
37832                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37833                 this.add(region, ret);
37834                 break;
37835             */
37836             
37837             case 'Nest': 
37838                 // create a new Layout (which is  a Border Layout...
37839                 
37840                 var clayout = cfg.layout;
37841                 clayout.el  = this.el.createChild();
37842                 clayout.items   = clayout.items  || [];
37843                 
37844                 delete cfg.layout;
37845                 
37846                 // replace this exitems with the clayout ones..
37847                 xitems = clayout.items;
37848                  
37849                 // force background off if it's in center...
37850                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37851                     cfg.background = false;
37852                 }
37853                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37854                 
37855                 
37856                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37857                 //console.log('adding nested layout panel '  + cfg.toSource());
37858                 this.add(region, ret);
37859                 nb = {}; /// find first...
37860                 break;
37861             
37862             case 'Grid':
37863                 
37864                 // needs grid and region
37865                 
37866                 //var el = this.getRegion(region).el.createChild();
37867                 /*
37868                  *var el = this.el.createChild();
37869                 // create the grid first...
37870                 cfg.grid.container = el;
37871                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37872                 */
37873                 
37874                 if (region == 'center' && this.active ) {
37875                     cfg.background = false;
37876                 }
37877                 
37878                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37879                 
37880                 this.add(region, ret);
37881                 /*
37882                 if (cfg.background) {
37883                     // render grid on panel activation (if panel background)
37884                     ret.on('activate', function(gp) {
37885                         if (!gp.grid.rendered) {
37886                     //        gp.grid.render(el);
37887                         }
37888                     });
37889                 } else {
37890                   //  cfg.grid.render(el);
37891                 }
37892                 */
37893                 break;
37894            
37895            
37896             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37897                 // it was the old xcomponent building that caused this before.
37898                 // espeically if border is the top element in the tree.
37899                 ret = this;
37900                 break; 
37901                 
37902                     
37903                 
37904                 
37905                 
37906             default:
37907                 /*
37908                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37909                     
37910                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37911                     this.add(region, ret);
37912                 } else {
37913                 */
37914                     Roo.log(cfg);
37915                     throw "Can not add '" + cfg.xtype + "' to Border";
37916                     return null;
37917              
37918                                 
37919              
37920         }
37921         this.beginUpdate();
37922         // add children..
37923         var region = '';
37924         var abn = {};
37925         Roo.each(xitems, function(i)  {
37926             region = nb && i.region ? i.region : false;
37927             
37928             var add = ret.addxtype(i);
37929            
37930             if (region) {
37931                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37932                 if (!i.background) {
37933                     abn[region] = nb[region] ;
37934                 }
37935             }
37936             
37937         });
37938         this.endUpdate();
37939
37940         // make the last non-background panel active..
37941         //if (nb) { Roo.log(abn); }
37942         if (nb) {
37943             
37944             for(var r in abn) {
37945                 region = this.getRegion(r);
37946                 if (region) {
37947                     // tried using nb[r], but it does not work..
37948                      
37949                     region.showPanel(abn[r]);
37950                    
37951                 }
37952             }
37953         }
37954         return ret;
37955         
37956     },
37957     
37958     
37959 // private
37960     factory : function(cfg)
37961     {
37962         
37963         var validRegions = Roo.bootstrap.layout.Border.regions;
37964
37965         var target = cfg.region;
37966         cfg.mgr = this;
37967         
37968         var r = Roo.bootstrap.layout;
37969         Roo.log(target);
37970         switch(target){
37971             case "north":
37972                 return new r.North(cfg);
37973             case "south":
37974                 return new r.South(cfg);
37975             case "east":
37976                 return new r.East(cfg);
37977             case "west":
37978                 return new r.West(cfg);
37979             case "center":
37980                 return new r.Center(cfg);
37981         }
37982         throw 'Layout region "'+target+'" not supported.';
37983     }
37984     
37985     
37986 });
37987  /*
37988  * Based on:
37989  * Ext JS Library 1.1.1
37990  * Copyright(c) 2006-2007, Ext JS, LLC.
37991  *
37992  * Originally Released Under LGPL - original licence link has changed is not relivant.
37993  *
37994  * Fork - LGPL
37995  * <script type="text/javascript">
37996  */
37997  
37998 /**
37999  * @class Roo.bootstrap.layout.Basic
38000  * @extends Roo.util.Observable
38001  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38002  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38003  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38004  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38005  * @cfg {string}   region  the region that it inhabits..
38006  * @cfg {bool}   skipConfig skip config?
38007  * 
38008
38009  */
38010 Roo.bootstrap.layout.Basic = function(config){
38011     
38012     this.mgr = config.mgr;
38013     
38014     this.position = config.region;
38015     
38016     var skipConfig = config.skipConfig;
38017     
38018     this.events = {
38019         /**
38020          * @scope Roo.BasicLayoutRegion
38021          */
38022         
38023         /**
38024          * @event beforeremove
38025          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38026          * @param {Roo.LayoutRegion} this
38027          * @param {Roo.ContentPanel} panel The panel
38028          * @param {Object} e The cancel event object
38029          */
38030         "beforeremove" : true,
38031         /**
38032          * @event invalidated
38033          * Fires when the layout for this region is changed.
38034          * @param {Roo.LayoutRegion} this
38035          */
38036         "invalidated" : true,
38037         /**
38038          * @event visibilitychange
38039          * Fires when this region is shown or hidden 
38040          * @param {Roo.LayoutRegion} this
38041          * @param {Boolean} visibility true or false
38042          */
38043         "visibilitychange" : true,
38044         /**
38045          * @event paneladded
38046          * Fires when a panel is added. 
38047          * @param {Roo.LayoutRegion} this
38048          * @param {Roo.ContentPanel} panel The panel
38049          */
38050         "paneladded" : true,
38051         /**
38052          * @event panelremoved
38053          * Fires when a panel is removed. 
38054          * @param {Roo.LayoutRegion} this
38055          * @param {Roo.ContentPanel} panel The panel
38056          */
38057         "panelremoved" : true,
38058         /**
38059          * @event beforecollapse
38060          * Fires when this region before collapse.
38061          * @param {Roo.LayoutRegion} this
38062          */
38063         "beforecollapse" : true,
38064         /**
38065          * @event collapsed
38066          * Fires when this region is collapsed.
38067          * @param {Roo.LayoutRegion} this
38068          */
38069         "collapsed" : true,
38070         /**
38071          * @event expanded
38072          * Fires when this region is expanded.
38073          * @param {Roo.LayoutRegion} this
38074          */
38075         "expanded" : true,
38076         /**
38077          * @event slideshow
38078          * Fires when this region is slid into view.
38079          * @param {Roo.LayoutRegion} this
38080          */
38081         "slideshow" : true,
38082         /**
38083          * @event slidehide
38084          * Fires when this region slides out of view. 
38085          * @param {Roo.LayoutRegion} this
38086          */
38087         "slidehide" : true,
38088         /**
38089          * @event panelactivated
38090          * Fires when a panel is activated. 
38091          * @param {Roo.LayoutRegion} this
38092          * @param {Roo.ContentPanel} panel The activated panel
38093          */
38094         "panelactivated" : true,
38095         /**
38096          * @event resized
38097          * Fires when the user resizes this region. 
38098          * @param {Roo.LayoutRegion} this
38099          * @param {Number} newSize The new size (width for east/west, height for north/south)
38100          */
38101         "resized" : true
38102     };
38103     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38104     this.panels = new Roo.util.MixedCollection();
38105     this.panels.getKey = this.getPanelId.createDelegate(this);
38106     this.box = null;
38107     this.activePanel = null;
38108     // ensure listeners are added...
38109     
38110     if (config.listeners || config.events) {
38111         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38112             listeners : config.listeners || {},
38113             events : config.events || {}
38114         });
38115     }
38116     
38117     if(skipConfig !== true){
38118         this.applyConfig(config);
38119     }
38120 };
38121
38122 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38123 {
38124     getPanelId : function(p){
38125         return p.getId();
38126     },
38127     
38128     applyConfig : function(config){
38129         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38130         this.config = config;
38131         
38132     },
38133     
38134     /**
38135      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38136      * the width, for horizontal (north, south) the height.
38137      * @param {Number} newSize The new width or height
38138      */
38139     resizeTo : function(newSize){
38140         var el = this.el ? this.el :
38141                  (this.activePanel ? this.activePanel.getEl() : null);
38142         if(el){
38143             switch(this.position){
38144                 case "east":
38145                 case "west":
38146                     el.setWidth(newSize);
38147                     this.fireEvent("resized", this, newSize);
38148                 break;
38149                 case "north":
38150                 case "south":
38151                     el.setHeight(newSize);
38152                     this.fireEvent("resized", this, newSize);
38153                 break;                
38154             }
38155         }
38156     },
38157     
38158     getBox : function(){
38159         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38160     },
38161     
38162     getMargins : function(){
38163         return this.margins;
38164     },
38165     
38166     updateBox : function(box){
38167         this.box = box;
38168         var el = this.activePanel.getEl();
38169         el.dom.style.left = box.x + "px";
38170         el.dom.style.top = box.y + "px";
38171         this.activePanel.setSize(box.width, box.height);
38172     },
38173     
38174     /**
38175      * Returns the container element for this region.
38176      * @return {Roo.Element}
38177      */
38178     getEl : function(){
38179         return this.activePanel;
38180     },
38181     
38182     /**
38183      * Returns true if this region is currently visible.
38184      * @return {Boolean}
38185      */
38186     isVisible : function(){
38187         return this.activePanel ? true : false;
38188     },
38189     
38190     setActivePanel : function(panel){
38191         panel = this.getPanel(panel);
38192         if(this.activePanel && this.activePanel != panel){
38193             this.activePanel.setActiveState(false);
38194             this.activePanel.getEl().setLeftTop(-10000,-10000);
38195         }
38196         this.activePanel = panel;
38197         panel.setActiveState(true);
38198         if(this.box){
38199             panel.setSize(this.box.width, this.box.height);
38200         }
38201         this.fireEvent("panelactivated", this, panel);
38202         this.fireEvent("invalidated");
38203     },
38204     
38205     /**
38206      * Show the specified panel.
38207      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38208      * @return {Roo.ContentPanel} The shown panel or null
38209      */
38210     showPanel : function(panel){
38211         panel = this.getPanel(panel);
38212         if(panel){
38213             this.setActivePanel(panel);
38214         }
38215         return panel;
38216     },
38217     
38218     /**
38219      * Get the active panel for this region.
38220      * @return {Roo.ContentPanel} The active panel or null
38221      */
38222     getActivePanel : function(){
38223         return this.activePanel;
38224     },
38225     
38226     /**
38227      * Add the passed ContentPanel(s)
38228      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38229      * @return {Roo.ContentPanel} The panel added (if only one was added)
38230      */
38231     add : function(panel){
38232         if(arguments.length > 1){
38233             for(var i = 0, len = arguments.length; i < len; i++) {
38234                 this.add(arguments[i]);
38235             }
38236             return null;
38237         }
38238         if(this.hasPanel(panel)){
38239             this.showPanel(panel);
38240             return panel;
38241         }
38242         var el = panel.getEl();
38243         if(el.dom.parentNode != this.mgr.el.dom){
38244             this.mgr.el.dom.appendChild(el.dom);
38245         }
38246         if(panel.setRegion){
38247             panel.setRegion(this);
38248         }
38249         this.panels.add(panel);
38250         el.setStyle("position", "absolute");
38251         if(!panel.background){
38252             this.setActivePanel(panel);
38253             if(this.config.initialSize && this.panels.getCount()==1){
38254                 this.resizeTo(this.config.initialSize);
38255             }
38256         }
38257         this.fireEvent("paneladded", this, panel);
38258         return panel;
38259     },
38260     
38261     /**
38262      * Returns true if the panel is in this region.
38263      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38264      * @return {Boolean}
38265      */
38266     hasPanel : function(panel){
38267         if(typeof panel == "object"){ // must be panel obj
38268             panel = panel.getId();
38269         }
38270         return this.getPanel(panel) ? true : false;
38271     },
38272     
38273     /**
38274      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38275      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38276      * @param {Boolean} preservePanel Overrides the config preservePanel option
38277      * @return {Roo.ContentPanel} The panel that was removed
38278      */
38279     remove : function(panel, preservePanel){
38280         panel = this.getPanel(panel);
38281         if(!panel){
38282             return null;
38283         }
38284         var e = {};
38285         this.fireEvent("beforeremove", this, panel, e);
38286         if(e.cancel === true){
38287             return null;
38288         }
38289         var panelId = panel.getId();
38290         this.panels.removeKey(panelId);
38291         return panel;
38292     },
38293     
38294     /**
38295      * Returns the panel specified or null if it's not in this region.
38296      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38297      * @return {Roo.ContentPanel}
38298      */
38299     getPanel : function(id){
38300         if(typeof id == "object"){ // must be panel obj
38301             return id;
38302         }
38303         return this.panels.get(id);
38304     },
38305     
38306     /**
38307      * Returns this regions position (north/south/east/west/center).
38308      * @return {String} 
38309      */
38310     getPosition: function(){
38311         return this.position;    
38312     }
38313 });/*
38314  * Based on:
38315  * Ext JS Library 1.1.1
38316  * Copyright(c) 2006-2007, Ext JS, LLC.
38317  *
38318  * Originally Released Under LGPL - original licence link has changed is not relivant.
38319  *
38320  * Fork - LGPL
38321  * <script type="text/javascript">
38322  */
38323  
38324 /**
38325  * @class Roo.bootstrap.layout.Region
38326  * @extends Roo.bootstrap.layout.Basic
38327  * This class represents a region in a layout manager.
38328  
38329  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38330  * @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})
38331  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38332  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38333  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38334  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38335  * @cfg {String}    title           The title for the region (overrides panel titles)
38336  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38337  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38338  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38339  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38340  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38341  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38342  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38343  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38344  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38345  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38346
38347  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38348  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38349  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38350  * @cfg {Number}    width           For East/West panels
38351  * @cfg {Number}    height          For North/South panels
38352  * @cfg {Boolean}   split           To show the splitter
38353  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38354  * 
38355  * @cfg {string}   cls             Extra CSS classes to add to region
38356  * 
38357  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38358  * @cfg {string}   region  the region that it inhabits..
38359  *
38360
38361  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38362  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38363
38364  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38365  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38366  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38367  */
38368 Roo.bootstrap.layout.Region = function(config)
38369 {
38370     this.applyConfig(config);
38371
38372     var mgr = config.mgr;
38373     var pos = config.region;
38374     config.skipConfig = true;
38375     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38376     
38377     if (mgr.el) {
38378         this.onRender(mgr.el);   
38379     }
38380      
38381     this.visible = true;
38382     this.collapsed = false;
38383     this.unrendered_panels = [];
38384 };
38385
38386 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38387
38388     position: '', // set by wrapper (eg. north/south etc..)
38389     unrendered_panels : null,  // unrendered panels.
38390     
38391     tabPosition : false,
38392     
38393     mgr: false, // points to 'Border'
38394     
38395     
38396     createBody : function(){
38397         /** This region's body element 
38398         * @type Roo.Element */
38399         this.bodyEl = this.el.createChild({
38400                 tag: "div",
38401                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38402         });
38403     },
38404
38405     onRender: function(ctr, pos)
38406     {
38407         var dh = Roo.DomHelper;
38408         /** This region's container element 
38409         * @type Roo.Element */
38410         this.el = dh.append(ctr.dom, {
38411                 tag: "div",
38412                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38413             }, true);
38414         /** This region's title element 
38415         * @type Roo.Element */
38416     
38417         this.titleEl = dh.append(this.el.dom,  {
38418                 tag: "div",
38419                 unselectable: "on",
38420                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38421                 children:[
38422                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38423                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38424                 ]
38425             }, true);
38426         
38427         this.titleEl.enableDisplayMode();
38428         /** This region's title text element 
38429         * @type HTMLElement */
38430         this.titleTextEl = this.titleEl.dom.firstChild;
38431         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38432         /*
38433         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38434         this.closeBtn.enableDisplayMode();
38435         this.closeBtn.on("click", this.closeClicked, this);
38436         this.closeBtn.hide();
38437     */
38438         this.createBody(this.config);
38439         if(this.config.hideWhenEmpty){
38440             this.hide();
38441             this.on("paneladded", this.validateVisibility, this);
38442             this.on("panelremoved", this.validateVisibility, this);
38443         }
38444         if(this.autoScroll){
38445             this.bodyEl.setStyle("overflow", "auto");
38446         }else{
38447             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38448         }
38449         //if(c.titlebar !== false){
38450             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38451                 this.titleEl.hide();
38452             }else{
38453                 this.titleEl.show();
38454                 if(this.config.title){
38455                     this.titleTextEl.innerHTML = this.config.title;
38456                 }
38457             }
38458         //}
38459         if(this.config.collapsed){
38460             this.collapse(true);
38461         }
38462         if(this.config.hidden){
38463             this.hide();
38464         }
38465         
38466         if (this.unrendered_panels && this.unrendered_panels.length) {
38467             for (var i =0;i< this.unrendered_panels.length; i++) {
38468                 this.add(this.unrendered_panels[i]);
38469             }
38470             this.unrendered_panels = null;
38471             
38472         }
38473         
38474     },
38475     
38476     applyConfig : function(c)
38477     {
38478         /*
38479          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38480             var dh = Roo.DomHelper;
38481             if(c.titlebar !== false){
38482                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38483                 this.collapseBtn.on("click", this.collapse, this);
38484                 this.collapseBtn.enableDisplayMode();
38485                 /*
38486                 if(c.showPin === true || this.showPin){
38487                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38488                     this.stickBtn.enableDisplayMode();
38489                     this.stickBtn.on("click", this.expand, this);
38490                     this.stickBtn.hide();
38491                 }
38492                 
38493             }
38494             */
38495             /** This region's collapsed element
38496             * @type Roo.Element */
38497             /*
38498              *
38499             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38500                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38501             ]}, true);
38502             
38503             if(c.floatable !== false){
38504                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38505                this.collapsedEl.on("click", this.collapseClick, this);
38506             }
38507
38508             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38509                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38510                    id: "message", unselectable: "on", style:{"float":"left"}});
38511                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38512              }
38513             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38514             this.expandBtn.on("click", this.expand, this);
38515             
38516         }
38517         
38518         if(this.collapseBtn){
38519             this.collapseBtn.setVisible(c.collapsible == true);
38520         }
38521         
38522         this.cmargins = c.cmargins || this.cmargins ||
38523                          (this.position == "west" || this.position == "east" ?
38524                              {top: 0, left: 2, right:2, bottom: 0} :
38525                              {top: 2, left: 0, right:0, bottom: 2});
38526         */
38527         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38528         
38529         
38530         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38531         
38532         this.autoScroll = c.autoScroll || false;
38533         
38534         
38535        
38536         
38537         this.duration = c.duration || .30;
38538         this.slideDuration = c.slideDuration || .45;
38539         this.config = c;
38540        
38541     },
38542     /**
38543      * Returns true if this region is currently visible.
38544      * @return {Boolean}
38545      */
38546     isVisible : function(){
38547         return this.visible;
38548     },
38549
38550     /**
38551      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38552      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38553      */
38554     //setCollapsedTitle : function(title){
38555     //    title = title || "&#160;";
38556      //   if(this.collapsedTitleTextEl){
38557       //      this.collapsedTitleTextEl.innerHTML = title;
38558        // }
38559     //},
38560
38561     getBox : function(){
38562         var b;
38563       //  if(!this.collapsed){
38564             b = this.el.getBox(false, true);
38565        // }else{
38566           //  b = this.collapsedEl.getBox(false, true);
38567         //}
38568         return b;
38569     },
38570
38571     getMargins : function(){
38572         return this.margins;
38573         //return this.collapsed ? this.cmargins : this.margins;
38574     },
38575 /*
38576     highlight : function(){
38577         this.el.addClass("x-layout-panel-dragover");
38578     },
38579
38580     unhighlight : function(){
38581         this.el.removeClass("x-layout-panel-dragover");
38582     },
38583 */
38584     updateBox : function(box)
38585     {
38586         if (!this.bodyEl) {
38587             return; // not rendered yet..
38588         }
38589         
38590         this.box = box;
38591         if(!this.collapsed){
38592             this.el.dom.style.left = box.x + "px";
38593             this.el.dom.style.top = box.y + "px";
38594             this.updateBody(box.width, box.height);
38595         }else{
38596             this.collapsedEl.dom.style.left = box.x + "px";
38597             this.collapsedEl.dom.style.top = box.y + "px";
38598             this.collapsedEl.setSize(box.width, box.height);
38599         }
38600         if(this.tabs){
38601             this.tabs.autoSizeTabs();
38602         }
38603     },
38604
38605     updateBody : function(w, h)
38606     {
38607         if(w !== null){
38608             this.el.setWidth(w);
38609             w -= this.el.getBorderWidth("rl");
38610             if(this.config.adjustments){
38611                 w += this.config.adjustments[0];
38612             }
38613         }
38614         if(h !== null && h > 0){
38615             this.el.setHeight(h);
38616             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38617             h -= this.el.getBorderWidth("tb");
38618             if(this.config.adjustments){
38619                 h += this.config.adjustments[1];
38620             }
38621             this.bodyEl.setHeight(h);
38622             if(this.tabs){
38623                 h = this.tabs.syncHeight(h);
38624             }
38625         }
38626         if(this.panelSize){
38627             w = w !== null ? w : this.panelSize.width;
38628             h = h !== null ? h : this.panelSize.height;
38629         }
38630         if(this.activePanel){
38631             var el = this.activePanel.getEl();
38632             w = w !== null ? w : el.getWidth();
38633             h = h !== null ? h : el.getHeight();
38634             this.panelSize = {width: w, height: h};
38635             this.activePanel.setSize(w, h);
38636         }
38637         if(Roo.isIE && this.tabs){
38638             this.tabs.el.repaint();
38639         }
38640     },
38641
38642     /**
38643      * Returns the container element for this region.
38644      * @return {Roo.Element}
38645      */
38646     getEl : function(){
38647         return this.el;
38648     },
38649
38650     /**
38651      * Hides this region.
38652      */
38653     hide : function(){
38654         //if(!this.collapsed){
38655             this.el.dom.style.left = "-2000px";
38656             this.el.hide();
38657         //}else{
38658          //   this.collapsedEl.dom.style.left = "-2000px";
38659          //   this.collapsedEl.hide();
38660        // }
38661         this.visible = false;
38662         this.fireEvent("visibilitychange", this, false);
38663     },
38664
38665     /**
38666      * Shows this region if it was previously hidden.
38667      */
38668     show : function(){
38669         //if(!this.collapsed){
38670             this.el.show();
38671         //}else{
38672         //    this.collapsedEl.show();
38673        // }
38674         this.visible = true;
38675         this.fireEvent("visibilitychange", this, true);
38676     },
38677 /*
38678     closeClicked : function(){
38679         if(this.activePanel){
38680             this.remove(this.activePanel);
38681         }
38682     },
38683
38684     collapseClick : function(e){
38685         if(this.isSlid){
38686            e.stopPropagation();
38687            this.slideIn();
38688         }else{
38689            e.stopPropagation();
38690            this.slideOut();
38691         }
38692     },
38693 */
38694     /**
38695      * Collapses this region.
38696      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38697      */
38698     /*
38699     collapse : function(skipAnim, skipCheck = false){
38700         if(this.collapsed) {
38701             return;
38702         }
38703         
38704         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38705             
38706             this.collapsed = true;
38707             if(this.split){
38708                 this.split.el.hide();
38709             }
38710             if(this.config.animate && skipAnim !== true){
38711                 this.fireEvent("invalidated", this);
38712                 this.animateCollapse();
38713             }else{
38714                 this.el.setLocation(-20000,-20000);
38715                 this.el.hide();
38716                 this.collapsedEl.show();
38717                 this.fireEvent("collapsed", this);
38718                 this.fireEvent("invalidated", this);
38719             }
38720         }
38721         
38722     },
38723 */
38724     animateCollapse : function(){
38725         // overridden
38726     },
38727
38728     /**
38729      * Expands this region if it was previously collapsed.
38730      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38731      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38732      */
38733     /*
38734     expand : function(e, skipAnim){
38735         if(e) {
38736             e.stopPropagation();
38737         }
38738         if(!this.collapsed || this.el.hasActiveFx()) {
38739             return;
38740         }
38741         if(this.isSlid){
38742             this.afterSlideIn();
38743             skipAnim = true;
38744         }
38745         this.collapsed = false;
38746         if(this.config.animate && skipAnim !== true){
38747             this.animateExpand();
38748         }else{
38749             this.el.show();
38750             if(this.split){
38751                 this.split.el.show();
38752             }
38753             this.collapsedEl.setLocation(-2000,-2000);
38754             this.collapsedEl.hide();
38755             this.fireEvent("invalidated", this);
38756             this.fireEvent("expanded", this);
38757         }
38758     },
38759 */
38760     animateExpand : function(){
38761         // overridden
38762     },
38763
38764     initTabs : function()
38765     {
38766         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38767         
38768         var ts = new Roo.bootstrap.panel.Tabs({
38769             el: this.bodyEl.dom,
38770             region : this,
38771             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38772             disableTooltips: this.config.disableTabTips,
38773             toolbar : this.config.toolbar
38774         });
38775         
38776         if(this.config.hideTabs){
38777             ts.stripWrap.setDisplayed(false);
38778         }
38779         this.tabs = ts;
38780         ts.resizeTabs = this.config.resizeTabs === true;
38781         ts.minTabWidth = this.config.minTabWidth || 40;
38782         ts.maxTabWidth = this.config.maxTabWidth || 250;
38783         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38784         ts.monitorResize = false;
38785         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38786         ts.bodyEl.addClass('roo-layout-tabs-body');
38787         this.panels.each(this.initPanelAsTab, this);
38788     },
38789
38790     initPanelAsTab : function(panel){
38791         var ti = this.tabs.addTab(
38792             panel.getEl().id,
38793             panel.getTitle(),
38794             null,
38795             this.config.closeOnTab && panel.isClosable(),
38796             panel.tpl
38797         );
38798         if(panel.tabTip !== undefined){
38799             ti.setTooltip(panel.tabTip);
38800         }
38801         ti.on("activate", function(){
38802               this.setActivePanel(panel);
38803         }, this);
38804         
38805         if(this.config.closeOnTab){
38806             ti.on("beforeclose", function(t, e){
38807                 e.cancel = true;
38808                 this.remove(panel);
38809             }, this);
38810         }
38811         
38812         panel.tabItem = ti;
38813         
38814         return ti;
38815     },
38816
38817     updatePanelTitle : function(panel, title)
38818     {
38819         if(this.activePanel == panel){
38820             this.updateTitle(title);
38821         }
38822         if(this.tabs){
38823             var ti = this.tabs.getTab(panel.getEl().id);
38824             ti.setText(title);
38825             if(panel.tabTip !== undefined){
38826                 ti.setTooltip(panel.tabTip);
38827             }
38828         }
38829     },
38830
38831     updateTitle : function(title){
38832         if(this.titleTextEl && !this.config.title){
38833             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38834         }
38835     },
38836
38837     setActivePanel : function(panel)
38838     {
38839         panel = this.getPanel(panel);
38840         if(this.activePanel && this.activePanel != panel){
38841             if(this.activePanel.setActiveState(false) === false){
38842                 return;
38843             }
38844         }
38845         this.activePanel = panel;
38846         panel.setActiveState(true);
38847         if(this.panelSize){
38848             panel.setSize(this.panelSize.width, this.panelSize.height);
38849         }
38850         if(this.closeBtn){
38851             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38852         }
38853         this.updateTitle(panel.getTitle());
38854         if(this.tabs){
38855             this.fireEvent("invalidated", this);
38856         }
38857         this.fireEvent("panelactivated", this, panel);
38858     },
38859
38860     /**
38861      * Shows the specified panel.
38862      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38863      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38864      */
38865     showPanel : function(panel)
38866     {
38867         panel = this.getPanel(panel);
38868         if(panel){
38869             if(this.tabs){
38870                 var tab = this.tabs.getTab(panel.getEl().id);
38871                 if(tab.isHidden()){
38872                     this.tabs.unhideTab(tab.id);
38873                 }
38874                 tab.activate();
38875             }else{
38876                 this.setActivePanel(panel);
38877             }
38878         }
38879         return panel;
38880     },
38881
38882     /**
38883      * Get the active panel for this region.
38884      * @return {Roo.ContentPanel} The active panel or null
38885      */
38886     getActivePanel : function(){
38887         return this.activePanel;
38888     },
38889
38890     validateVisibility : function(){
38891         if(this.panels.getCount() < 1){
38892             this.updateTitle("&#160;");
38893             this.closeBtn.hide();
38894             this.hide();
38895         }else{
38896             if(!this.isVisible()){
38897                 this.show();
38898             }
38899         }
38900     },
38901
38902     /**
38903      * Adds the passed ContentPanel(s) to this region.
38904      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38905      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38906      */
38907     add : function(panel)
38908     {
38909         if(arguments.length > 1){
38910             for(var i = 0, len = arguments.length; i < len; i++) {
38911                 this.add(arguments[i]);
38912             }
38913             return null;
38914         }
38915         
38916         // if we have not been rendered yet, then we can not really do much of this..
38917         if (!this.bodyEl) {
38918             this.unrendered_panels.push(panel);
38919             return panel;
38920         }
38921         
38922         
38923         
38924         
38925         if(this.hasPanel(panel)){
38926             this.showPanel(panel);
38927             return panel;
38928         }
38929         panel.setRegion(this);
38930         this.panels.add(panel);
38931        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38932             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38933             // and hide them... ???
38934             this.bodyEl.dom.appendChild(panel.getEl().dom);
38935             if(panel.background !== true){
38936                 this.setActivePanel(panel);
38937             }
38938             this.fireEvent("paneladded", this, panel);
38939             return panel;
38940         }
38941         */
38942         if(!this.tabs){
38943             this.initTabs();
38944         }else{
38945             this.initPanelAsTab(panel);
38946         }
38947         
38948         
38949         if(panel.background !== true){
38950             this.tabs.activate(panel.getEl().id);
38951         }
38952         this.fireEvent("paneladded", this, panel);
38953         return panel;
38954     },
38955
38956     /**
38957      * Hides the tab for the specified panel.
38958      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38959      */
38960     hidePanel : function(panel){
38961         if(this.tabs && (panel = this.getPanel(panel))){
38962             this.tabs.hideTab(panel.getEl().id);
38963         }
38964     },
38965
38966     /**
38967      * Unhides the tab for a previously hidden panel.
38968      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38969      */
38970     unhidePanel : function(panel){
38971         if(this.tabs && (panel = this.getPanel(panel))){
38972             this.tabs.unhideTab(panel.getEl().id);
38973         }
38974     },
38975
38976     clearPanels : function(){
38977         while(this.panels.getCount() > 0){
38978              this.remove(this.panels.first());
38979         }
38980     },
38981
38982     /**
38983      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38984      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38985      * @param {Boolean} preservePanel Overrides the config preservePanel option
38986      * @return {Roo.ContentPanel} The panel that was removed
38987      */
38988     remove : function(panel, preservePanel)
38989     {
38990         panel = this.getPanel(panel);
38991         if(!panel){
38992             return null;
38993         }
38994         var e = {};
38995         this.fireEvent("beforeremove", this, panel, e);
38996         if(e.cancel === true){
38997             return null;
38998         }
38999         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39000         var panelId = panel.getId();
39001         this.panels.removeKey(panelId);
39002         if(preservePanel){
39003             document.body.appendChild(panel.getEl().dom);
39004         }
39005         if(this.tabs){
39006             this.tabs.removeTab(panel.getEl().id);
39007         }else if (!preservePanel){
39008             this.bodyEl.dom.removeChild(panel.getEl().dom);
39009         }
39010         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39011             var p = this.panels.first();
39012             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39013             tempEl.appendChild(p.getEl().dom);
39014             this.bodyEl.update("");
39015             this.bodyEl.dom.appendChild(p.getEl().dom);
39016             tempEl = null;
39017             this.updateTitle(p.getTitle());
39018             this.tabs = null;
39019             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39020             this.setActivePanel(p);
39021         }
39022         panel.setRegion(null);
39023         if(this.activePanel == panel){
39024             this.activePanel = null;
39025         }
39026         if(this.config.autoDestroy !== false && preservePanel !== true){
39027             try{panel.destroy();}catch(e){}
39028         }
39029         this.fireEvent("panelremoved", this, panel);
39030         return panel;
39031     },
39032
39033     /**
39034      * Returns the TabPanel component used by this region
39035      * @return {Roo.TabPanel}
39036      */
39037     getTabs : function(){
39038         return this.tabs;
39039     },
39040
39041     createTool : function(parentEl, className){
39042         var btn = Roo.DomHelper.append(parentEl, {
39043             tag: "div",
39044             cls: "x-layout-tools-button",
39045             children: [ {
39046                 tag: "div",
39047                 cls: "roo-layout-tools-button-inner " + className,
39048                 html: "&#160;"
39049             }]
39050         }, true);
39051         btn.addClassOnOver("roo-layout-tools-button-over");
39052         return btn;
39053     }
39054 });/*
39055  * Based on:
39056  * Ext JS Library 1.1.1
39057  * Copyright(c) 2006-2007, Ext JS, LLC.
39058  *
39059  * Originally Released Under LGPL - original licence link has changed is not relivant.
39060  *
39061  * Fork - LGPL
39062  * <script type="text/javascript">
39063  */
39064  
39065
39066
39067 /**
39068  * @class Roo.SplitLayoutRegion
39069  * @extends Roo.LayoutRegion
39070  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39071  */
39072 Roo.bootstrap.layout.Split = function(config){
39073     this.cursor = config.cursor;
39074     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39075 };
39076
39077 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39078 {
39079     splitTip : "Drag to resize.",
39080     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39081     useSplitTips : false,
39082
39083     applyConfig : function(config){
39084         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39085     },
39086     
39087     onRender : function(ctr,pos) {
39088         
39089         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39090         if(!this.config.split){
39091             return;
39092         }
39093         if(!this.split){
39094             
39095             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39096                             tag: "div",
39097                             id: this.el.id + "-split",
39098                             cls: "roo-layout-split roo-layout-split-"+this.position,
39099                             html: "&#160;"
39100             });
39101             /** The SplitBar for this region 
39102             * @type Roo.SplitBar */
39103             // does not exist yet...
39104             Roo.log([this.position, this.orientation]);
39105             
39106             this.split = new Roo.bootstrap.SplitBar({
39107                 dragElement : splitEl,
39108                 resizingElement: this.el,
39109                 orientation : this.orientation
39110             });
39111             
39112             this.split.on("moved", this.onSplitMove, this);
39113             this.split.useShim = this.config.useShim === true;
39114             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39115             if(this.useSplitTips){
39116                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39117             }
39118             //if(config.collapsible){
39119             //    this.split.el.on("dblclick", this.collapse,  this);
39120             //}
39121         }
39122         if(typeof this.config.minSize != "undefined"){
39123             this.split.minSize = this.config.minSize;
39124         }
39125         if(typeof this.config.maxSize != "undefined"){
39126             this.split.maxSize = this.config.maxSize;
39127         }
39128         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39129             this.hideSplitter();
39130         }
39131         
39132     },
39133
39134     getHMaxSize : function(){
39135          var cmax = this.config.maxSize || 10000;
39136          var center = this.mgr.getRegion("center");
39137          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39138     },
39139
39140     getVMaxSize : function(){
39141          var cmax = this.config.maxSize || 10000;
39142          var center = this.mgr.getRegion("center");
39143          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39144     },
39145
39146     onSplitMove : function(split, newSize){
39147         this.fireEvent("resized", this, newSize);
39148     },
39149     
39150     /** 
39151      * Returns the {@link Roo.SplitBar} for this region.
39152      * @return {Roo.SplitBar}
39153      */
39154     getSplitBar : function(){
39155         return this.split;
39156     },
39157     
39158     hide : function(){
39159         this.hideSplitter();
39160         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39161     },
39162
39163     hideSplitter : function(){
39164         if(this.split){
39165             this.split.el.setLocation(-2000,-2000);
39166             this.split.el.hide();
39167         }
39168     },
39169
39170     show : function(){
39171         if(this.split){
39172             this.split.el.show();
39173         }
39174         Roo.bootstrap.layout.Split.superclass.show.call(this);
39175     },
39176     
39177     beforeSlide: function(){
39178         if(Roo.isGecko){// firefox overflow auto bug workaround
39179             this.bodyEl.clip();
39180             if(this.tabs) {
39181                 this.tabs.bodyEl.clip();
39182             }
39183             if(this.activePanel){
39184                 this.activePanel.getEl().clip();
39185                 
39186                 if(this.activePanel.beforeSlide){
39187                     this.activePanel.beforeSlide();
39188                 }
39189             }
39190         }
39191     },
39192     
39193     afterSlide : function(){
39194         if(Roo.isGecko){// firefox overflow auto bug workaround
39195             this.bodyEl.unclip();
39196             if(this.tabs) {
39197                 this.tabs.bodyEl.unclip();
39198             }
39199             if(this.activePanel){
39200                 this.activePanel.getEl().unclip();
39201                 if(this.activePanel.afterSlide){
39202                     this.activePanel.afterSlide();
39203                 }
39204             }
39205         }
39206     },
39207
39208     initAutoHide : function(){
39209         if(this.autoHide !== false){
39210             if(!this.autoHideHd){
39211                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39212                 this.autoHideHd = {
39213                     "mouseout": function(e){
39214                         if(!e.within(this.el, true)){
39215                             st.delay(500);
39216                         }
39217                     },
39218                     "mouseover" : function(e){
39219                         st.cancel();
39220                     },
39221                     scope : this
39222                 };
39223             }
39224             this.el.on(this.autoHideHd);
39225         }
39226     },
39227
39228     clearAutoHide : function(){
39229         if(this.autoHide !== false){
39230             this.el.un("mouseout", this.autoHideHd.mouseout);
39231             this.el.un("mouseover", this.autoHideHd.mouseover);
39232         }
39233     },
39234
39235     clearMonitor : function(){
39236         Roo.get(document).un("click", this.slideInIf, this);
39237     },
39238
39239     // these names are backwards but not changed for compat
39240     slideOut : function(){
39241         if(this.isSlid || this.el.hasActiveFx()){
39242             return;
39243         }
39244         this.isSlid = true;
39245         if(this.collapseBtn){
39246             this.collapseBtn.hide();
39247         }
39248         this.closeBtnState = this.closeBtn.getStyle('display');
39249         this.closeBtn.hide();
39250         if(this.stickBtn){
39251             this.stickBtn.show();
39252         }
39253         this.el.show();
39254         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39255         this.beforeSlide();
39256         this.el.setStyle("z-index", 10001);
39257         this.el.slideIn(this.getSlideAnchor(), {
39258             callback: function(){
39259                 this.afterSlide();
39260                 this.initAutoHide();
39261                 Roo.get(document).on("click", this.slideInIf, this);
39262                 this.fireEvent("slideshow", this);
39263             },
39264             scope: this,
39265             block: true
39266         });
39267     },
39268
39269     afterSlideIn : function(){
39270         this.clearAutoHide();
39271         this.isSlid = false;
39272         this.clearMonitor();
39273         this.el.setStyle("z-index", "");
39274         if(this.collapseBtn){
39275             this.collapseBtn.show();
39276         }
39277         this.closeBtn.setStyle('display', this.closeBtnState);
39278         if(this.stickBtn){
39279             this.stickBtn.hide();
39280         }
39281         this.fireEvent("slidehide", this);
39282     },
39283
39284     slideIn : function(cb){
39285         if(!this.isSlid || this.el.hasActiveFx()){
39286             Roo.callback(cb);
39287             return;
39288         }
39289         this.isSlid = false;
39290         this.beforeSlide();
39291         this.el.slideOut(this.getSlideAnchor(), {
39292             callback: function(){
39293                 this.el.setLeftTop(-10000, -10000);
39294                 this.afterSlide();
39295                 this.afterSlideIn();
39296                 Roo.callback(cb);
39297             },
39298             scope: this,
39299             block: true
39300         });
39301     },
39302     
39303     slideInIf : function(e){
39304         if(!e.within(this.el)){
39305             this.slideIn();
39306         }
39307     },
39308
39309     animateCollapse : function(){
39310         this.beforeSlide();
39311         this.el.setStyle("z-index", 20000);
39312         var anchor = this.getSlideAnchor();
39313         this.el.slideOut(anchor, {
39314             callback : function(){
39315                 this.el.setStyle("z-index", "");
39316                 this.collapsedEl.slideIn(anchor, {duration:.3});
39317                 this.afterSlide();
39318                 this.el.setLocation(-10000,-10000);
39319                 this.el.hide();
39320                 this.fireEvent("collapsed", this);
39321             },
39322             scope: this,
39323             block: true
39324         });
39325     },
39326
39327     animateExpand : function(){
39328         this.beforeSlide();
39329         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39330         this.el.setStyle("z-index", 20000);
39331         this.collapsedEl.hide({
39332             duration:.1
39333         });
39334         this.el.slideIn(this.getSlideAnchor(), {
39335             callback : function(){
39336                 this.el.setStyle("z-index", "");
39337                 this.afterSlide();
39338                 if(this.split){
39339                     this.split.el.show();
39340                 }
39341                 this.fireEvent("invalidated", this);
39342                 this.fireEvent("expanded", this);
39343             },
39344             scope: this,
39345             block: true
39346         });
39347     },
39348
39349     anchors : {
39350         "west" : "left",
39351         "east" : "right",
39352         "north" : "top",
39353         "south" : "bottom"
39354     },
39355
39356     sanchors : {
39357         "west" : "l",
39358         "east" : "r",
39359         "north" : "t",
39360         "south" : "b"
39361     },
39362
39363     canchors : {
39364         "west" : "tl-tr",
39365         "east" : "tr-tl",
39366         "north" : "tl-bl",
39367         "south" : "bl-tl"
39368     },
39369
39370     getAnchor : function(){
39371         return this.anchors[this.position];
39372     },
39373
39374     getCollapseAnchor : function(){
39375         return this.canchors[this.position];
39376     },
39377
39378     getSlideAnchor : function(){
39379         return this.sanchors[this.position];
39380     },
39381
39382     getAlignAdj : function(){
39383         var cm = this.cmargins;
39384         switch(this.position){
39385             case "west":
39386                 return [0, 0];
39387             break;
39388             case "east":
39389                 return [0, 0];
39390             break;
39391             case "north":
39392                 return [0, 0];
39393             break;
39394             case "south":
39395                 return [0, 0];
39396             break;
39397         }
39398     },
39399
39400     getExpandAdj : function(){
39401         var c = this.collapsedEl, cm = this.cmargins;
39402         switch(this.position){
39403             case "west":
39404                 return [-(cm.right+c.getWidth()+cm.left), 0];
39405             break;
39406             case "east":
39407                 return [cm.right+c.getWidth()+cm.left, 0];
39408             break;
39409             case "north":
39410                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39411             break;
39412             case "south":
39413                 return [0, cm.top+cm.bottom+c.getHeight()];
39414             break;
39415         }
39416     }
39417 });/*
39418  * Based on:
39419  * Ext JS Library 1.1.1
39420  * Copyright(c) 2006-2007, Ext JS, LLC.
39421  *
39422  * Originally Released Under LGPL - original licence link has changed is not relivant.
39423  *
39424  * Fork - LGPL
39425  * <script type="text/javascript">
39426  */
39427 /*
39428  * These classes are private internal classes
39429  */
39430 Roo.bootstrap.layout.Center = function(config){
39431     config.region = "center";
39432     Roo.bootstrap.layout.Region.call(this, config);
39433     this.visible = true;
39434     this.minWidth = config.minWidth || 20;
39435     this.minHeight = config.minHeight || 20;
39436 };
39437
39438 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39439     hide : function(){
39440         // center panel can't be hidden
39441     },
39442     
39443     show : function(){
39444         // center panel can't be hidden
39445     },
39446     
39447     getMinWidth: function(){
39448         return this.minWidth;
39449     },
39450     
39451     getMinHeight: function(){
39452         return this.minHeight;
39453     }
39454 });
39455
39456
39457
39458
39459  
39460
39461
39462
39463
39464
39465
39466 Roo.bootstrap.layout.North = function(config)
39467 {
39468     config.region = 'north';
39469     config.cursor = 'n-resize';
39470     
39471     Roo.bootstrap.layout.Split.call(this, config);
39472     
39473     
39474     if(this.split){
39475         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39476         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39477         this.split.el.addClass("roo-layout-split-v");
39478     }
39479     //var size = config.initialSize || config.height;
39480     //if(this.el && typeof size != "undefined"){
39481     //    this.el.setHeight(size);
39482     //}
39483 };
39484 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39485 {
39486     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39487      
39488      
39489     onRender : function(ctr, pos)
39490     {
39491         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39492         var size = this.config.initialSize || this.config.height;
39493         if(this.el && typeof size != "undefined"){
39494             this.el.setHeight(size);
39495         }
39496     
39497     },
39498     
39499     getBox : function(){
39500         if(this.collapsed){
39501             return this.collapsedEl.getBox();
39502         }
39503         var box = this.el.getBox();
39504         if(this.split){
39505             box.height += this.split.el.getHeight();
39506         }
39507         return box;
39508     },
39509     
39510     updateBox : function(box){
39511         if(this.split && !this.collapsed){
39512             box.height -= this.split.el.getHeight();
39513             this.split.el.setLeft(box.x);
39514             this.split.el.setTop(box.y+box.height);
39515             this.split.el.setWidth(box.width);
39516         }
39517         if(this.collapsed){
39518             this.updateBody(box.width, null);
39519         }
39520         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39521     }
39522 });
39523
39524
39525
39526
39527
39528 Roo.bootstrap.layout.South = function(config){
39529     config.region = 'south';
39530     config.cursor = 's-resize';
39531     Roo.bootstrap.layout.Split.call(this, config);
39532     if(this.split){
39533         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39534         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39535         this.split.el.addClass("roo-layout-split-v");
39536     }
39537     
39538 };
39539
39540 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39541     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39542     
39543     onRender : function(ctr, pos)
39544     {
39545         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39546         var size = this.config.initialSize || this.config.height;
39547         if(this.el && typeof size != "undefined"){
39548             this.el.setHeight(size);
39549         }
39550     
39551     },
39552     
39553     getBox : function(){
39554         if(this.collapsed){
39555             return this.collapsedEl.getBox();
39556         }
39557         var box = this.el.getBox();
39558         if(this.split){
39559             var sh = this.split.el.getHeight();
39560             box.height += sh;
39561             box.y -= sh;
39562         }
39563         return box;
39564     },
39565     
39566     updateBox : function(box){
39567         if(this.split && !this.collapsed){
39568             var sh = this.split.el.getHeight();
39569             box.height -= sh;
39570             box.y += sh;
39571             this.split.el.setLeft(box.x);
39572             this.split.el.setTop(box.y-sh);
39573             this.split.el.setWidth(box.width);
39574         }
39575         if(this.collapsed){
39576             this.updateBody(box.width, null);
39577         }
39578         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39579     }
39580 });
39581
39582 Roo.bootstrap.layout.East = function(config){
39583     config.region = "east";
39584     config.cursor = "e-resize";
39585     Roo.bootstrap.layout.Split.call(this, config);
39586     if(this.split){
39587         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39588         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39589         this.split.el.addClass("roo-layout-split-h");
39590     }
39591     
39592 };
39593 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39594     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39595     
39596     onRender : function(ctr, pos)
39597     {
39598         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39599         var size = this.config.initialSize || this.config.width;
39600         if(this.el && typeof size != "undefined"){
39601             this.el.setWidth(size);
39602         }
39603     
39604     },
39605     
39606     getBox : function(){
39607         if(this.collapsed){
39608             return this.collapsedEl.getBox();
39609         }
39610         var box = this.el.getBox();
39611         if(this.split){
39612             var sw = this.split.el.getWidth();
39613             box.width += sw;
39614             box.x -= sw;
39615         }
39616         return box;
39617     },
39618
39619     updateBox : function(box){
39620         if(this.split && !this.collapsed){
39621             var sw = this.split.el.getWidth();
39622             box.width -= sw;
39623             this.split.el.setLeft(box.x);
39624             this.split.el.setTop(box.y);
39625             this.split.el.setHeight(box.height);
39626             box.x += sw;
39627         }
39628         if(this.collapsed){
39629             this.updateBody(null, box.height);
39630         }
39631         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39632     }
39633 });
39634
39635 Roo.bootstrap.layout.West = function(config){
39636     config.region = "west";
39637     config.cursor = "w-resize";
39638     
39639     Roo.bootstrap.layout.Split.call(this, config);
39640     if(this.split){
39641         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39642         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39643         this.split.el.addClass("roo-layout-split-h");
39644     }
39645     
39646 };
39647 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39648     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39649     
39650     onRender: function(ctr, pos)
39651     {
39652         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39653         var size = this.config.initialSize || this.config.width;
39654         if(typeof size != "undefined"){
39655             this.el.setWidth(size);
39656         }
39657     },
39658     
39659     getBox : function(){
39660         if(this.collapsed){
39661             return this.collapsedEl.getBox();
39662         }
39663         var box = this.el.getBox();
39664         if (box.width == 0) {
39665             box.width = this.config.width; // kludge?
39666         }
39667         if(this.split){
39668             box.width += this.split.el.getWidth();
39669         }
39670         return box;
39671     },
39672     
39673     updateBox : function(box){
39674         if(this.split && !this.collapsed){
39675             var sw = this.split.el.getWidth();
39676             box.width -= sw;
39677             this.split.el.setLeft(box.x+box.width);
39678             this.split.el.setTop(box.y);
39679             this.split.el.setHeight(box.height);
39680         }
39681         if(this.collapsed){
39682             this.updateBody(null, box.height);
39683         }
39684         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39685     }
39686 });Roo.namespace("Roo.bootstrap.panel");/*
39687  * Based on:
39688  * Ext JS Library 1.1.1
39689  * Copyright(c) 2006-2007, Ext JS, LLC.
39690  *
39691  * Originally Released Under LGPL - original licence link has changed is not relivant.
39692  *
39693  * Fork - LGPL
39694  * <script type="text/javascript">
39695  */
39696 /**
39697  * @class Roo.ContentPanel
39698  * @extends Roo.util.Observable
39699  * A basic ContentPanel element.
39700  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39701  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39702  * @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
39703  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39704  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39705  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39706  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39707  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39708  * @cfg {String} title          The title for this panel
39709  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39710  * @cfg {String} url            Calls {@link #setUrl} with this value
39711  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39712  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39713  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39714  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39715  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39716  * @cfg {Boolean} badges render the badges
39717  * @cfg {String} cls  extra classes to use  
39718  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39719
39720  * @constructor
39721  * Create a new ContentPanel.
39722  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39723  * @param {String/Object} config A string to set only the title or a config object
39724  * @param {String} content (optional) Set the HTML content for this panel
39725  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39726  */
39727 Roo.bootstrap.panel.Content = function( config){
39728     
39729     this.tpl = config.tpl || false;
39730     
39731     var el = config.el;
39732     var content = config.content;
39733
39734     if(config.autoCreate){ // xtype is available if this is called from factory
39735         el = Roo.id();
39736     }
39737     this.el = Roo.get(el);
39738     if(!this.el && config && config.autoCreate){
39739         if(typeof config.autoCreate == "object"){
39740             if(!config.autoCreate.id){
39741                 config.autoCreate.id = config.id||el;
39742             }
39743             this.el = Roo.DomHelper.append(document.body,
39744                         config.autoCreate, true);
39745         }else{
39746             var elcfg =  {
39747                 tag: "div",
39748                 cls: (config.cls || '') +
39749                     (config.background ? ' bg-' + config.background : '') +
39750                     " roo-layout-inactive-content",
39751                 id: config.id||el
39752             };
39753             if (config.iframe) {
39754                 elcfg.cn = [
39755                     {
39756                         tag : 'iframe',
39757                         style : 'border: 0px',
39758                         src : 'about:blank'
39759                     }
39760                 ];
39761             }
39762               
39763             if (config.html) {
39764                 elcfg.html = config.html;
39765                 
39766             }
39767                         
39768             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39769             if (config.iframe) {
39770                 this.iframeEl = this.el.select('iframe',true).first();
39771             }
39772             
39773         }
39774     } 
39775     this.closable = false;
39776     this.loaded = false;
39777     this.active = false;
39778    
39779       
39780     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39781         
39782         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39783         
39784         this.wrapEl = this.el; //this.el.wrap();
39785         var ti = [];
39786         if (config.toolbar.items) {
39787             ti = config.toolbar.items ;
39788             delete config.toolbar.items ;
39789         }
39790         
39791         var nitems = [];
39792         this.toolbar.render(this.wrapEl, 'before');
39793         for(var i =0;i < ti.length;i++) {
39794           //  Roo.log(['add child', items[i]]);
39795             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39796         }
39797         this.toolbar.items = nitems;
39798         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39799         delete config.toolbar;
39800         
39801     }
39802     /*
39803     // xtype created footer. - not sure if will work as we normally have to render first..
39804     if (this.footer && !this.footer.el && this.footer.xtype) {
39805         if (!this.wrapEl) {
39806             this.wrapEl = this.el.wrap();
39807         }
39808     
39809         this.footer.container = this.wrapEl.createChild();
39810          
39811         this.footer = Roo.factory(this.footer, Roo);
39812         
39813     }
39814     */
39815     
39816      if(typeof config == "string"){
39817         this.title = config;
39818     }else{
39819         Roo.apply(this, config);
39820     }
39821     
39822     if(this.resizeEl){
39823         this.resizeEl = Roo.get(this.resizeEl, true);
39824     }else{
39825         this.resizeEl = this.el;
39826     }
39827     // handle view.xtype
39828     
39829  
39830     
39831     
39832     this.addEvents({
39833         /**
39834          * @event activate
39835          * Fires when this panel is activated. 
39836          * @param {Roo.ContentPanel} this
39837          */
39838         "activate" : true,
39839         /**
39840          * @event deactivate
39841          * Fires when this panel is activated. 
39842          * @param {Roo.ContentPanel} this
39843          */
39844         "deactivate" : true,
39845
39846         /**
39847          * @event resize
39848          * Fires when this panel is resized if fitToFrame is true.
39849          * @param {Roo.ContentPanel} this
39850          * @param {Number} width The width after any component adjustments
39851          * @param {Number} height The height after any component adjustments
39852          */
39853         "resize" : true,
39854         
39855          /**
39856          * @event render
39857          * Fires when this tab is created
39858          * @param {Roo.ContentPanel} this
39859          */
39860         "render" : true
39861         
39862         
39863         
39864     });
39865     
39866
39867     
39868     
39869     if(this.autoScroll && !this.iframe){
39870         this.resizeEl.setStyle("overflow", "auto");
39871     } else {
39872         // fix randome scrolling
39873         //this.el.on('scroll', function() {
39874         //    Roo.log('fix random scolling');
39875         //    this.scrollTo('top',0); 
39876         //});
39877     }
39878     content = content || this.content;
39879     if(content){
39880         this.setContent(content);
39881     }
39882     if(config && config.url){
39883         this.setUrl(this.url, this.params, this.loadOnce);
39884     }
39885     
39886     
39887     
39888     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39889     
39890     if (this.view && typeof(this.view.xtype) != 'undefined') {
39891         this.view.el = this.el.appendChild(document.createElement("div"));
39892         this.view = Roo.factory(this.view); 
39893         this.view.render  &&  this.view.render(false, '');  
39894     }
39895     
39896     
39897     this.fireEvent('render', this);
39898 };
39899
39900 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39901     
39902     cls : '',
39903     background : '',
39904     
39905     tabTip : '',
39906     
39907     iframe : false,
39908     iframeEl : false,
39909     
39910     setRegion : function(region){
39911         this.region = region;
39912         this.setActiveClass(region && !this.background);
39913     },
39914     
39915     
39916     setActiveClass: function(state)
39917     {
39918         if(state){
39919            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39920            this.el.setStyle('position','relative');
39921         }else{
39922            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39923            this.el.setStyle('position', 'absolute');
39924         } 
39925     },
39926     
39927     /**
39928      * Returns the toolbar for this Panel if one was configured. 
39929      * @return {Roo.Toolbar} 
39930      */
39931     getToolbar : function(){
39932         return this.toolbar;
39933     },
39934     
39935     setActiveState : function(active)
39936     {
39937         this.active = active;
39938         this.setActiveClass(active);
39939         if(!active){
39940             if(this.fireEvent("deactivate", this) === false){
39941                 return false;
39942             }
39943             return true;
39944         }
39945         this.fireEvent("activate", this);
39946         return true;
39947     },
39948     /**
39949      * Updates this panel's element (not for iframe)
39950      * @param {String} content The new content
39951      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39952     */
39953     setContent : function(content, loadScripts){
39954         if (this.iframe) {
39955             return;
39956         }
39957         
39958         this.el.update(content, loadScripts);
39959     },
39960
39961     ignoreResize : function(w, h){
39962         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39963             return true;
39964         }else{
39965             this.lastSize = {width: w, height: h};
39966             return false;
39967         }
39968     },
39969     /**
39970      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39971      * @return {Roo.UpdateManager} The UpdateManager
39972      */
39973     getUpdateManager : function(){
39974         if (this.iframe) {
39975             return false;
39976         }
39977         return this.el.getUpdateManager();
39978     },
39979      /**
39980      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39981      * Does not work with IFRAME contents
39982      * @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:
39983 <pre><code>
39984 panel.load({
39985     url: "your-url.php",
39986     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39987     callback: yourFunction,
39988     scope: yourObject, //(optional scope)
39989     discardUrl: false,
39990     nocache: false,
39991     text: "Loading...",
39992     timeout: 30,
39993     scripts: false
39994 });
39995 </code></pre>
39996      
39997      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39998      * 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.
39999      * @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}
40000      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40001      * @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.
40002      * @return {Roo.ContentPanel} this
40003      */
40004     load : function(){
40005         
40006         if (this.iframe) {
40007             return this;
40008         }
40009         
40010         var um = this.el.getUpdateManager();
40011         um.update.apply(um, arguments);
40012         return this;
40013     },
40014
40015
40016     /**
40017      * 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.
40018      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40019      * @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)
40020      * @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)
40021      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40022      */
40023     setUrl : function(url, params, loadOnce){
40024         if (this.iframe) {
40025             this.iframeEl.dom.src = url;
40026             return false;
40027         }
40028         
40029         if(this.refreshDelegate){
40030             this.removeListener("activate", this.refreshDelegate);
40031         }
40032         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40033         this.on("activate", this.refreshDelegate);
40034         return this.el.getUpdateManager();
40035     },
40036     
40037     _handleRefresh : function(url, params, loadOnce){
40038         if(!loadOnce || !this.loaded){
40039             var updater = this.el.getUpdateManager();
40040             updater.update(url, params, this._setLoaded.createDelegate(this));
40041         }
40042     },
40043     
40044     _setLoaded : function(){
40045         this.loaded = true;
40046     }, 
40047     
40048     /**
40049      * Returns this panel's id
40050      * @return {String} 
40051      */
40052     getId : function(){
40053         return this.el.id;
40054     },
40055     
40056     /** 
40057      * Returns this panel's element - used by regiosn to add.
40058      * @return {Roo.Element} 
40059      */
40060     getEl : function(){
40061         return this.wrapEl || this.el;
40062     },
40063     
40064    
40065     
40066     adjustForComponents : function(width, height)
40067     {
40068         //Roo.log('adjustForComponents ');
40069         if(this.resizeEl != this.el){
40070             width -= this.el.getFrameWidth('lr');
40071             height -= this.el.getFrameWidth('tb');
40072         }
40073         if(this.toolbar){
40074             var te = this.toolbar.getEl();
40075             te.setWidth(width);
40076             height -= te.getHeight();
40077         }
40078         if(this.footer){
40079             var te = this.footer.getEl();
40080             te.setWidth(width);
40081             height -= te.getHeight();
40082         }
40083         
40084         
40085         if(this.adjustments){
40086             width += this.adjustments[0];
40087             height += this.adjustments[1];
40088         }
40089         return {"width": width, "height": height};
40090     },
40091     
40092     setSize : function(width, height){
40093         if(this.fitToFrame && !this.ignoreResize(width, height)){
40094             if(this.fitContainer && this.resizeEl != this.el){
40095                 this.el.setSize(width, height);
40096             }
40097             var size = this.adjustForComponents(width, height);
40098             if (this.iframe) {
40099                 this.iframeEl.setSize(width,height);
40100             }
40101             
40102             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40103             this.fireEvent('resize', this, size.width, size.height);
40104             
40105             
40106         }
40107     },
40108     
40109     /**
40110      * Returns this panel's title
40111      * @return {String} 
40112      */
40113     getTitle : function(){
40114         
40115         if (typeof(this.title) != 'object') {
40116             return this.title;
40117         }
40118         
40119         var t = '';
40120         for (var k in this.title) {
40121             if (!this.title.hasOwnProperty(k)) {
40122                 continue;
40123             }
40124             
40125             if (k.indexOf('-') >= 0) {
40126                 var s = k.split('-');
40127                 for (var i = 0; i<s.length; i++) {
40128                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40129                 }
40130             } else {
40131                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40132             }
40133         }
40134         return t;
40135     },
40136     
40137     /**
40138      * Set this panel's title
40139      * @param {String} title
40140      */
40141     setTitle : function(title){
40142         this.title = title;
40143         if(this.region){
40144             this.region.updatePanelTitle(this, title);
40145         }
40146     },
40147     
40148     /**
40149      * Returns true is this panel was configured to be closable
40150      * @return {Boolean} 
40151      */
40152     isClosable : function(){
40153         return this.closable;
40154     },
40155     
40156     beforeSlide : function(){
40157         this.el.clip();
40158         this.resizeEl.clip();
40159     },
40160     
40161     afterSlide : function(){
40162         this.el.unclip();
40163         this.resizeEl.unclip();
40164     },
40165     
40166     /**
40167      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40168      *   Will fail silently if the {@link #setUrl} method has not been called.
40169      *   This does not activate the panel, just updates its content.
40170      */
40171     refresh : function(){
40172         if(this.refreshDelegate){
40173            this.loaded = false;
40174            this.refreshDelegate();
40175         }
40176     },
40177     
40178     /**
40179      * Destroys this panel
40180      */
40181     destroy : function(){
40182         this.el.removeAllListeners();
40183         var tempEl = document.createElement("span");
40184         tempEl.appendChild(this.el.dom);
40185         tempEl.innerHTML = "";
40186         this.el.remove();
40187         this.el = null;
40188     },
40189     
40190     /**
40191      * form - if the content panel contains a form - this is a reference to it.
40192      * @type {Roo.form.Form}
40193      */
40194     form : false,
40195     /**
40196      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40197      *    This contains a reference to it.
40198      * @type {Roo.View}
40199      */
40200     view : false,
40201     
40202       /**
40203      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40204      * <pre><code>
40205
40206 layout.addxtype({
40207        xtype : 'Form',
40208        items: [ .... ]
40209    }
40210 );
40211
40212 </code></pre>
40213      * @param {Object} cfg Xtype definition of item to add.
40214      */
40215     
40216     
40217     getChildContainer: function () {
40218         return this.getEl();
40219     }
40220     
40221     
40222     /*
40223         var  ret = new Roo.factory(cfg);
40224         return ret;
40225         
40226         
40227         // add form..
40228         if (cfg.xtype.match(/^Form$/)) {
40229             
40230             var el;
40231             //if (this.footer) {
40232             //    el = this.footer.container.insertSibling(false, 'before');
40233             //} else {
40234                 el = this.el.createChild();
40235             //}
40236
40237             this.form = new  Roo.form.Form(cfg);
40238             
40239             
40240             if ( this.form.allItems.length) {
40241                 this.form.render(el.dom);
40242             }
40243             return this.form;
40244         }
40245         // should only have one of theses..
40246         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40247             // views.. should not be just added - used named prop 'view''
40248             
40249             cfg.el = this.el.appendChild(document.createElement("div"));
40250             // factory?
40251             
40252             var ret = new Roo.factory(cfg);
40253              
40254              ret.render && ret.render(false, ''); // render blank..
40255             this.view = ret;
40256             return ret;
40257         }
40258         return false;
40259     }
40260     \*/
40261 });
40262  
40263 /**
40264  * @class Roo.bootstrap.panel.Grid
40265  * @extends Roo.bootstrap.panel.Content
40266  * @constructor
40267  * Create a new GridPanel.
40268  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40269  * @param {Object} config A the config object
40270   
40271  */
40272
40273
40274
40275 Roo.bootstrap.panel.Grid = function(config)
40276 {
40277     
40278       
40279     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40280         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40281
40282     config.el = this.wrapper;
40283     //this.el = this.wrapper;
40284     
40285       if (config.container) {
40286         // ctor'ed from a Border/panel.grid
40287         
40288         
40289         this.wrapper.setStyle("overflow", "hidden");
40290         this.wrapper.addClass('roo-grid-container');
40291
40292     }
40293     
40294     
40295     if(config.toolbar){
40296         var tool_el = this.wrapper.createChild();    
40297         this.toolbar = Roo.factory(config.toolbar);
40298         var ti = [];
40299         if (config.toolbar.items) {
40300             ti = config.toolbar.items ;
40301             delete config.toolbar.items ;
40302         }
40303         
40304         var nitems = [];
40305         this.toolbar.render(tool_el);
40306         for(var i =0;i < ti.length;i++) {
40307           //  Roo.log(['add child', items[i]]);
40308             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40309         }
40310         this.toolbar.items = nitems;
40311         
40312         delete config.toolbar;
40313     }
40314     
40315     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40316     config.grid.scrollBody = true;;
40317     config.grid.monitorWindowResize = false; // turn off autosizing
40318     config.grid.autoHeight = false;
40319     config.grid.autoWidth = false;
40320     
40321     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40322     
40323     if (config.background) {
40324         // render grid on panel activation (if panel background)
40325         this.on('activate', function(gp) {
40326             if (!gp.grid.rendered) {
40327                 gp.grid.render(this.wrapper);
40328                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40329             }
40330         });
40331             
40332     } else {
40333         this.grid.render(this.wrapper);
40334         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40335
40336     }
40337     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40338     // ??? needed ??? config.el = this.wrapper;
40339     
40340     
40341     
40342   
40343     // xtype created footer. - not sure if will work as we normally have to render first..
40344     if (this.footer && !this.footer.el && this.footer.xtype) {
40345         
40346         var ctr = this.grid.getView().getFooterPanel(true);
40347         this.footer.dataSource = this.grid.dataSource;
40348         this.footer = Roo.factory(this.footer, Roo);
40349         this.footer.render(ctr);
40350         
40351     }
40352     
40353     
40354     
40355     
40356      
40357 };
40358
40359 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40360     getId : function(){
40361         return this.grid.id;
40362     },
40363     
40364     /**
40365      * Returns the grid for this panel
40366      * @return {Roo.bootstrap.Table} 
40367      */
40368     getGrid : function(){
40369         return this.grid;    
40370     },
40371     
40372     setSize : function(width, height){
40373         if(!this.ignoreResize(width, height)){
40374             var grid = this.grid;
40375             var size = this.adjustForComponents(width, height);
40376             // tfoot is not a footer?
40377           
40378             
40379             var gridel = grid.getGridEl();
40380             gridel.setSize(size.width, size.height);
40381             
40382             var tbd = grid.getGridEl().select('tbody', true).first();
40383             var thd = grid.getGridEl().select('thead',true).first();
40384             var tbf= grid.getGridEl().select('tfoot', true).first();
40385
40386             if (tbf) {
40387                 size.height -= tbf.getHeight();
40388             }
40389             if (thd) {
40390                 size.height -= thd.getHeight();
40391             }
40392             
40393             tbd.setSize(size.width, size.height );
40394             // this is for the account management tab -seems to work there.
40395             var thd = grid.getGridEl().select('thead',true).first();
40396             //if (tbd) {
40397             //    tbd.setSize(size.width, size.height - thd.getHeight());
40398             //}
40399              
40400             grid.autoSize();
40401         }
40402     },
40403      
40404     
40405     
40406     beforeSlide : function(){
40407         this.grid.getView().scroller.clip();
40408     },
40409     
40410     afterSlide : function(){
40411         this.grid.getView().scroller.unclip();
40412     },
40413     
40414     destroy : function(){
40415         this.grid.destroy();
40416         delete this.grid;
40417         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40418     }
40419 });
40420
40421 /**
40422  * @class Roo.bootstrap.panel.Nest
40423  * @extends Roo.bootstrap.panel.Content
40424  * @constructor
40425  * Create a new Panel, that can contain a layout.Border.
40426  * 
40427  * 
40428  * @param {Roo.BorderLayout} layout The layout for this panel
40429  * @param {String/Object} config A string to set only the title or a config object
40430  */
40431 Roo.bootstrap.panel.Nest = function(config)
40432 {
40433     // construct with only one argument..
40434     /* FIXME - implement nicer consturctors
40435     if (layout.layout) {
40436         config = layout;
40437         layout = config.layout;
40438         delete config.layout;
40439     }
40440     if (layout.xtype && !layout.getEl) {
40441         // then layout needs constructing..
40442         layout = Roo.factory(layout, Roo);
40443     }
40444     */
40445     
40446     config.el =  config.layout.getEl();
40447     
40448     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40449     
40450     config.layout.monitorWindowResize = false; // turn off autosizing
40451     this.layout = config.layout;
40452     this.layout.getEl().addClass("roo-layout-nested-layout");
40453     this.layout.parent = this;
40454     
40455     
40456     
40457     
40458 };
40459
40460 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40461
40462     setSize : function(width, height){
40463         if(!this.ignoreResize(width, height)){
40464             var size = this.adjustForComponents(width, height);
40465             var el = this.layout.getEl();
40466             if (size.height < 1) {
40467                 el.setWidth(size.width);   
40468             } else {
40469                 el.setSize(size.width, size.height);
40470             }
40471             var touch = el.dom.offsetWidth;
40472             this.layout.layout();
40473             // ie requires a double layout on the first pass
40474             if(Roo.isIE && !this.initialized){
40475                 this.initialized = true;
40476                 this.layout.layout();
40477             }
40478         }
40479     },
40480     
40481     // activate all subpanels if not currently active..
40482     
40483     setActiveState : function(active){
40484         this.active = active;
40485         this.setActiveClass(active);
40486         
40487         if(!active){
40488             this.fireEvent("deactivate", this);
40489             return;
40490         }
40491         
40492         this.fireEvent("activate", this);
40493         // not sure if this should happen before or after..
40494         if (!this.layout) {
40495             return; // should not happen..
40496         }
40497         var reg = false;
40498         for (var r in this.layout.regions) {
40499             reg = this.layout.getRegion(r);
40500             if (reg.getActivePanel()) {
40501                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40502                 reg.setActivePanel(reg.getActivePanel());
40503                 continue;
40504             }
40505             if (!reg.panels.length) {
40506                 continue;
40507             }
40508             reg.showPanel(reg.getPanel(0));
40509         }
40510         
40511         
40512         
40513         
40514     },
40515     
40516     /**
40517      * Returns the nested BorderLayout for this panel
40518      * @return {Roo.BorderLayout} 
40519      */
40520     getLayout : function(){
40521         return this.layout;
40522     },
40523     
40524      /**
40525      * Adds a xtype elements to the layout of the nested panel
40526      * <pre><code>
40527
40528 panel.addxtype({
40529        xtype : 'ContentPanel',
40530        region: 'west',
40531        items: [ .... ]
40532    }
40533 );
40534
40535 panel.addxtype({
40536         xtype : 'NestedLayoutPanel',
40537         region: 'west',
40538         layout: {
40539            center: { },
40540            west: { }   
40541         },
40542         items : [ ... list of content panels or nested layout panels.. ]
40543    }
40544 );
40545 </code></pre>
40546      * @param {Object} cfg Xtype definition of item to add.
40547      */
40548     addxtype : function(cfg) {
40549         return this.layout.addxtype(cfg);
40550     
40551     }
40552 });/*
40553  * Based on:
40554  * Ext JS Library 1.1.1
40555  * Copyright(c) 2006-2007, Ext JS, LLC.
40556  *
40557  * Originally Released Under LGPL - original licence link has changed is not relivant.
40558  *
40559  * Fork - LGPL
40560  * <script type="text/javascript">
40561  */
40562 /**
40563  * @class Roo.TabPanel
40564  * @extends Roo.util.Observable
40565  * A lightweight tab container.
40566  * <br><br>
40567  * Usage:
40568  * <pre><code>
40569 // basic tabs 1, built from existing content
40570 var tabs = new Roo.TabPanel("tabs1");
40571 tabs.addTab("script", "View Script");
40572 tabs.addTab("markup", "View Markup");
40573 tabs.activate("script");
40574
40575 // more advanced tabs, built from javascript
40576 var jtabs = new Roo.TabPanel("jtabs");
40577 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40578
40579 // set up the UpdateManager
40580 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40581 var updater = tab2.getUpdateManager();
40582 updater.setDefaultUrl("ajax1.htm");
40583 tab2.on('activate', updater.refresh, updater, true);
40584
40585 // Use setUrl for Ajax loading
40586 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40587 tab3.setUrl("ajax2.htm", null, true);
40588
40589 // Disabled tab
40590 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40591 tab4.disable();
40592
40593 jtabs.activate("jtabs-1");
40594  * </code></pre>
40595  * @constructor
40596  * Create a new TabPanel.
40597  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40598  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40599  */
40600 Roo.bootstrap.panel.Tabs = function(config){
40601     /**
40602     * The container element for this TabPanel.
40603     * @type Roo.Element
40604     */
40605     this.el = Roo.get(config.el);
40606     delete config.el;
40607     if(config){
40608         if(typeof config == "boolean"){
40609             this.tabPosition = config ? "bottom" : "top";
40610         }else{
40611             Roo.apply(this, config);
40612         }
40613     }
40614     
40615     if(this.tabPosition == "bottom"){
40616         // if tabs are at the bottom = create the body first.
40617         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40618         this.el.addClass("roo-tabs-bottom");
40619     }
40620     // next create the tabs holders
40621     
40622     if (this.tabPosition == "west"){
40623         
40624         var reg = this.region; // fake it..
40625         while (reg) {
40626             if (!reg.mgr.parent) {
40627                 break;
40628             }
40629             reg = reg.mgr.parent.region;
40630         }
40631         Roo.log("got nest?");
40632         Roo.log(reg);
40633         if (reg.mgr.getRegion('west')) {
40634             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40635             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40636             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40637             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40638             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40639         
40640             
40641         }
40642         
40643         
40644     } else {
40645      
40646         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40647         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40648         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40649         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40650     }
40651     
40652     
40653     if(Roo.isIE){
40654         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40655     }
40656     
40657     // finally - if tabs are at the top, then create the body last..
40658     if(this.tabPosition != "bottom"){
40659         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40660          * @type Roo.Element
40661          */
40662         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40663         this.el.addClass("roo-tabs-top");
40664     }
40665     this.items = [];
40666
40667     this.bodyEl.setStyle("position", "relative");
40668
40669     this.active = null;
40670     this.activateDelegate = this.activate.createDelegate(this);
40671
40672     this.addEvents({
40673         /**
40674          * @event tabchange
40675          * Fires when the active tab changes
40676          * @param {Roo.TabPanel} this
40677          * @param {Roo.TabPanelItem} activePanel The new active tab
40678          */
40679         "tabchange": true,
40680         /**
40681          * @event beforetabchange
40682          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40683          * @param {Roo.TabPanel} this
40684          * @param {Object} e Set cancel to true on this object to cancel the tab change
40685          * @param {Roo.TabPanelItem} tab The tab being changed to
40686          */
40687         "beforetabchange" : true
40688     });
40689
40690     Roo.EventManager.onWindowResize(this.onResize, this);
40691     this.cpad = this.el.getPadding("lr");
40692     this.hiddenCount = 0;
40693
40694
40695     // toolbar on the tabbar support...
40696     if (this.toolbar) {
40697         alert("no toolbar support yet");
40698         this.toolbar  = false;
40699         /*
40700         var tcfg = this.toolbar;
40701         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40702         this.toolbar = new Roo.Toolbar(tcfg);
40703         if (Roo.isSafari) {
40704             var tbl = tcfg.container.child('table', true);
40705             tbl.setAttribute('width', '100%');
40706         }
40707         */
40708         
40709     }
40710    
40711
40712
40713     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40714 };
40715
40716 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40717     /*
40718      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40719      */
40720     tabPosition : "top",
40721     /*
40722      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40723      */
40724     currentTabWidth : 0,
40725     /*
40726      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40727      */
40728     minTabWidth : 40,
40729     /*
40730      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40731      */
40732     maxTabWidth : 250,
40733     /*
40734      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40735      */
40736     preferredTabWidth : 175,
40737     /*
40738      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40739      */
40740     resizeTabs : false,
40741     /*
40742      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40743      */
40744     monitorResize : true,
40745     /*
40746      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40747      */
40748     toolbar : false,  // set by caller..
40749     
40750     region : false, /// set by caller
40751     
40752     disableTooltips : true, // not used yet...
40753
40754     /**
40755      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40756      * @param {String} id The id of the div to use <b>or create</b>
40757      * @param {String} text The text for the tab
40758      * @param {String} content (optional) Content to put in the TabPanelItem body
40759      * @param {Boolean} closable (optional) True to create a close icon on the tab
40760      * @return {Roo.TabPanelItem} The created TabPanelItem
40761      */
40762     addTab : function(id, text, content, closable, tpl)
40763     {
40764         var item = new Roo.bootstrap.panel.TabItem({
40765             panel: this,
40766             id : id,
40767             text : text,
40768             closable : closable,
40769             tpl : tpl
40770         });
40771         this.addTabItem(item);
40772         if(content){
40773             item.setContent(content);
40774         }
40775         return item;
40776     },
40777
40778     /**
40779      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40780      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40781      * @return {Roo.TabPanelItem}
40782      */
40783     getTab : function(id){
40784         return this.items[id];
40785     },
40786
40787     /**
40788      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40789      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40790      */
40791     hideTab : function(id){
40792         var t = this.items[id];
40793         if(!t.isHidden()){
40794            t.setHidden(true);
40795            this.hiddenCount++;
40796            this.autoSizeTabs();
40797         }
40798     },
40799
40800     /**
40801      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40802      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40803      */
40804     unhideTab : function(id){
40805         var t = this.items[id];
40806         if(t.isHidden()){
40807            t.setHidden(false);
40808            this.hiddenCount--;
40809            this.autoSizeTabs();
40810         }
40811     },
40812
40813     /**
40814      * Adds an existing {@link Roo.TabPanelItem}.
40815      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40816      */
40817     addTabItem : function(item)
40818     {
40819         this.items[item.id] = item;
40820         this.items.push(item);
40821         this.autoSizeTabs();
40822       //  if(this.resizeTabs){
40823     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40824   //         this.autoSizeTabs();
40825 //        }else{
40826 //            item.autoSize();
40827        // }
40828     },
40829
40830     /**
40831      * Removes a {@link Roo.TabPanelItem}.
40832      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40833      */
40834     removeTab : function(id){
40835         var items = this.items;
40836         var tab = items[id];
40837         if(!tab) { return; }
40838         var index = items.indexOf(tab);
40839         if(this.active == tab && items.length > 1){
40840             var newTab = this.getNextAvailable(index);
40841             if(newTab) {
40842                 newTab.activate();
40843             }
40844         }
40845         this.stripEl.dom.removeChild(tab.pnode.dom);
40846         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40847             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40848         }
40849         items.splice(index, 1);
40850         delete this.items[tab.id];
40851         tab.fireEvent("close", tab);
40852         tab.purgeListeners();
40853         this.autoSizeTabs();
40854     },
40855
40856     getNextAvailable : function(start){
40857         var items = this.items;
40858         var index = start;
40859         // look for a next tab that will slide over to
40860         // replace the one being removed
40861         while(index < items.length){
40862             var item = items[++index];
40863             if(item && !item.isHidden()){
40864                 return item;
40865             }
40866         }
40867         // if one isn't found select the previous tab (on the left)
40868         index = start;
40869         while(index >= 0){
40870             var item = items[--index];
40871             if(item && !item.isHidden()){
40872                 return item;
40873             }
40874         }
40875         return null;
40876     },
40877
40878     /**
40879      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40880      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40881      */
40882     disableTab : function(id){
40883         var tab = this.items[id];
40884         if(tab && this.active != tab){
40885             tab.disable();
40886         }
40887     },
40888
40889     /**
40890      * Enables a {@link Roo.TabPanelItem} that is disabled.
40891      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40892      */
40893     enableTab : function(id){
40894         var tab = this.items[id];
40895         tab.enable();
40896     },
40897
40898     /**
40899      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40900      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40901      * @return {Roo.TabPanelItem} The TabPanelItem.
40902      */
40903     activate : function(id)
40904     {
40905         //Roo.log('activite:'  + id);
40906         
40907         var tab = this.items[id];
40908         if(!tab){
40909             return null;
40910         }
40911         if(tab == this.active || tab.disabled){
40912             return tab;
40913         }
40914         var e = {};
40915         this.fireEvent("beforetabchange", this, e, tab);
40916         if(e.cancel !== true && !tab.disabled){
40917             if(this.active){
40918                 this.active.hide();
40919             }
40920             this.active = this.items[id];
40921             this.active.show();
40922             this.fireEvent("tabchange", this, this.active);
40923         }
40924         return tab;
40925     },
40926
40927     /**
40928      * Gets the active {@link Roo.TabPanelItem}.
40929      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40930      */
40931     getActiveTab : function(){
40932         return this.active;
40933     },
40934
40935     /**
40936      * Updates the tab body element to fit the height of the container element
40937      * for overflow scrolling
40938      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40939      */
40940     syncHeight : function(targetHeight){
40941         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40942         var bm = this.bodyEl.getMargins();
40943         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40944         this.bodyEl.setHeight(newHeight);
40945         return newHeight;
40946     },
40947
40948     onResize : function(){
40949         if(this.monitorResize){
40950             this.autoSizeTabs();
40951         }
40952     },
40953
40954     /**
40955      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40956      */
40957     beginUpdate : function(){
40958         this.updating = true;
40959     },
40960
40961     /**
40962      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40963      */
40964     endUpdate : function(){
40965         this.updating = false;
40966         this.autoSizeTabs();
40967     },
40968
40969     /**
40970      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40971      */
40972     autoSizeTabs : function()
40973     {
40974         var count = this.items.length;
40975         var vcount = count - this.hiddenCount;
40976         
40977         if (vcount < 2) {
40978             this.stripEl.hide();
40979         } else {
40980             this.stripEl.show();
40981         }
40982         
40983         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40984             return;
40985         }
40986         
40987         
40988         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40989         var availWidth = Math.floor(w / vcount);
40990         var b = this.stripBody;
40991         if(b.getWidth() > w){
40992             var tabs = this.items;
40993             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40994             if(availWidth < this.minTabWidth){
40995                 /*if(!this.sleft){    // incomplete scrolling code
40996                     this.createScrollButtons();
40997                 }
40998                 this.showScroll();
40999                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41000             }
41001         }else{
41002             if(this.currentTabWidth < this.preferredTabWidth){
41003                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41004             }
41005         }
41006     },
41007
41008     /**
41009      * Returns the number of tabs in this TabPanel.
41010      * @return {Number}
41011      */
41012      getCount : function(){
41013          return this.items.length;
41014      },
41015
41016     /**
41017      * Resizes all the tabs to the passed width
41018      * @param {Number} The new width
41019      */
41020     setTabWidth : function(width){
41021         this.currentTabWidth = width;
41022         for(var i = 0, len = this.items.length; i < len; i++) {
41023                 if(!this.items[i].isHidden()) {
41024                 this.items[i].setWidth(width);
41025             }
41026         }
41027     },
41028
41029     /**
41030      * Destroys this TabPanel
41031      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41032      */
41033     destroy : function(removeEl){
41034         Roo.EventManager.removeResizeListener(this.onResize, this);
41035         for(var i = 0, len = this.items.length; i < len; i++){
41036             this.items[i].purgeListeners();
41037         }
41038         if(removeEl === true){
41039             this.el.update("");
41040             this.el.remove();
41041         }
41042     },
41043     
41044     createStrip : function(container)
41045     {
41046         var strip = document.createElement("nav");
41047         strip.className = Roo.bootstrap.version == 4 ?
41048             "navbar-light bg-light" : 
41049             "navbar navbar-default"; //"x-tabs-wrap";
41050         container.appendChild(strip);
41051         return strip;
41052     },
41053     
41054     createStripList : function(strip)
41055     {
41056         // div wrapper for retard IE
41057         // returns the "tr" element.
41058         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41059         //'<div class="x-tabs-strip-wrap">'+
41060           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41061           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41062         return strip.firstChild; //.firstChild.firstChild.firstChild;
41063     },
41064     createBody : function(container)
41065     {
41066         var body = document.createElement("div");
41067         Roo.id(body, "tab-body");
41068         //Roo.fly(body).addClass("x-tabs-body");
41069         Roo.fly(body).addClass("tab-content");
41070         container.appendChild(body);
41071         return body;
41072     },
41073     createItemBody :function(bodyEl, id){
41074         var body = Roo.getDom(id);
41075         if(!body){
41076             body = document.createElement("div");
41077             body.id = id;
41078         }
41079         //Roo.fly(body).addClass("x-tabs-item-body");
41080         Roo.fly(body).addClass("tab-pane");
41081          bodyEl.insertBefore(body, bodyEl.firstChild);
41082         return body;
41083     },
41084     /** @private */
41085     createStripElements :  function(stripEl, text, closable, tpl)
41086     {
41087         var td = document.createElement("li"); // was td..
41088         td.className = 'nav-item';
41089         
41090         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41091         
41092         
41093         stripEl.appendChild(td);
41094         /*if(closable){
41095             td.className = "x-tabs-closable";
41096             if(!this.closeTpl){
41097                 this.closeTpl = new Roo.Template(
41098                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41099                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41100                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41101                 );
41102             }
41103             var el = this.closeTpl.overwrite(td, {"text": text});
41104             var close = el.getElementsByTagName("div")[0];
41105             var inner = el.getElementsByTagName("em")[0];
41106             return {"el": el, "close": close, "inner": inner};
41107         } else {
41108         */
41109         // not sure what this is..
41110 //            if(!this.tabTpl){
41111                 //this.tabTpl = new Roo.Template(
41112                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41113                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41114                 //);
41115 //                this.tabTpl = new Roo.Template(
41116 //                   '<a href="#">' +
41117 //                   '<span unselectable="on"' +
41118 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41119 //                            ' >{text}</span></a>'
41120 //                );
41121 //                
41122 //            }
41123
41124
41125             var template = tpl || this.tabTpl || false;
41126             
41127             if(!template){
41128                 template =  new Roo.Template(
41129                         Roo.bootstrap.version == 4 ? 
41130                             (
41131                                 '<a class="nav-link" href="#" unselectable="on"' +
41132                                      (this.disableTooltips ? '' : ' title="{text}"') +
41133                                      ' >{text}</a>'
41134                             ) : (
41135                                 '<a class="nav-link" href="#">' +
41136                                 '<span unselectable="on"' +
41137                                          (this.disableTooltips ? '' : ' title="{text}"') +
41138                                     ' >{text}</span></a>'
41139                             )
41140                 );
41141             }
41142             
41143             switch (typeof(template)) {
41144                 case 'object' :
41145                     break;
41146                 case 'string' :
41147                     template = new Roo.Template(template);
41148                     break;
41149                 default :
41150                     break;
41151             }
41152             
41153             var el = template.overwrite(td, {"text": text});
41154             
41155             var inner = el.getElementsByTagName("span")[0];
41156             
41157             return {"el": el, "inner": inner};
41158             
41159     }
41160         
41161     
41162 });
41163
41164 /**
41165  * @class Roo.TabPanelItem
41166  * @extends Roo.util.Observable
41167  * Represents an individual item (tab plus body) in a TabPanel.
41168  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41169  * @param {String} id The id of this TabPanelItem
41170  * @param {String} text The text for the tab of this TabPanelItem
41171  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41172  */
41173 Roo.bootstrap.panel.TabItem = function(config){
41174     /**
41175      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41176      * @type Roo.TabPanel
41177      */
41178     this.tabPanel = config.panel;
41179     /**
41180      * The id for this TabPanelItem
41181      * @type String
41182      */
41183     this.id = config.id;
41184     /** @private */
41185     this.disabled = false;
41186     /** @private */
41187     this.text = config.text;
41188     /** @private */
41189     this.loaded = false;
41190     this.closable = config.closable;
41191
41192     /**
41193      * The body element for this TabPanelItem.
41194      * @type Roo.Element
41195      */
41196     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41197     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41198     this.bodyEl.setStyle("display", "block");
41199     this.bodyEl.setStyle("zoom", "1");
41200     //this.hideAction();
41201
41202     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41203     /** @private */
41204     this.el = Roo.get(els.el);
41205     this.inner = Roo.get(els.inner, true);
41206      this.textEl = Roo.bootstrap.version == 4 ?
41207         this.el : Roo.get(this.el.dom.firstChild, true);
41208
41209     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41210     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41211
41212     
41213 //    this.el.on("mousedown", this.onTabMouseDown, this);
41214     this.el.on("click", this.onTabClick, this);
41215     /** @private */
41216     if(config.closable){
41217         var c = Roo.get(els.close, true);
41218         c.dom.title = this.closeText;
41219         c.addClassOnOver("close-over");
41220         c.on("click", this.closeClick, this);
41221      }
41222
41223     this.addEvents({
41224          /**
41225          * @event activate
41226          * Fires when this tab becomes the active tab.
41227          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41228          * @param {Roo.TabPanelItem} this
41229          */
41230         "activate": true,
41231         /**
41232          * @event beforeclose
41233          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41234          * @param {Roo.TabPanelItem} this
41235          * @param {Object} e Set cancel to true on this object to cancel the close.
41236          */
41237         "beforeclose": true,
41238         /**
41239          * @event close
41240          * Fires when this tab is closed.
41241          * @param {Roo.TabPanelItem} this
41242          */
41243          "close": true,
41244         /**
41245          * @event deactivate
41246          * Fires when this tab is no longer the active tab.
41247          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41248          * @param {Roo.TabPanelItem} this
41249          */
41250          "deactivate" : true
41251     });
41252     this.hidden = false;
41253
41254     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41255 };
41256
41257 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41258            {
41259     purgeListeners : function(){
41260        Roo.util.Observable.prototype.purgeListeners.call(this);
41261        this.el.removeAllListeners();
41262     },
41263     /**
41264      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41265      */
41266     show : function(){
41267         this.status_node.addClass("active");
41268         this.showAction();
41269         if(Roo.isOpera){
41270             this.tabPanel.stripWrap.repaint();
41271         }
41272         this.fireEvent("activate", this.tabPanel, this);
41273     },
41274
41275     /**
41276      * Returns true if this tab is the active tab.
41277      * @return {Boolean}
41278      */
41279     isActive : function(){
41280         return this.tabPanel.getActiveTab() == this;
41281     },
41282
41283     /**
41284      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41285      */
41286     hide : function(){
41287         this.status_node.removeClass("active");
41288         this.hideAction();
41289         this.fireEvent("deactivate", this.tabPanel, this);
41290     },
41291
41292     hideAction : function(){
41293         this.bodyEl.hide();
41294         this.bodyEl.setStyle("position", "absolute");
41295         this.bodyEl.setLeft("-20000px");
41296         this.bodyEl.setTop("-20000px");
41297     },
41298
41299     showAction : function(){
41300         this.bodyEl.setStyle("position", "relative");
41301         this.bodyEl.setTop("");
41302         this.bodyEl.setLeft("");
41303         this.bodyEl.show();
41304     },
41305
41306     /**
41307      * Set the tooltip for the tab.
41308      * @param {String} tooltip The tab's tooltip
41309      */
41310     setTooltip : function(text){
41311         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41312             this.textEl.dom.qtip = text;
41313             this.textEl.dom.removeAttribute('title');
41314         }else{
41315             this.textEl.dom.title = text;
41316         }
41317     },
41318
41319     onTabClick : function(e){
41320         e.preventDefault();
41321         this.tabPanel.activate(this.id);
41322     },
41323
41324     onTabMouseDown : function(e){
41325         e.preventDefault();
41326         this.tabPanel.activate(this.id);
41327     },
41328 /*
41329     getWidth : function(){
41330         return this.inner.getWidth();
41331     },
41332
41333     setWidth : function(width){
41334         var iwidth = width - this.linode.getPadding("lr");
41335         this.inner.setWidth(iwidth);
41336         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41337         this.linode.setWidth(width);
41338     },
41339 */
41340     /**
41341      * Show or hide the tab
41342      * @param {Boolean} hidden True to hide or false to show.
41343      */
41344     setHidden : function(hidden){
41345         this.hidden = hidden;
41346         this.linode.setStyle("display", hidden ? "none" : "");
41347     },
41348
41349     /**
41350      * Returns true if this tab is "hidden"
41351      * @return {Boolean}
41352      */
41353     isHidden : function(){
41354         return this.hidden;
41355     },
41356
41357     /**
41358      * Returns the text for this tab
41359      * @return {String}
41360      */
41361     getText : function(){
41362         return this.text;
41363     },
41364     /*
41365     autoSize : function(){
41366         //this.el.beginMeasure();
41367         this.textEl.setWidth(1);
41368         /*
41369          *  #2804 [new] Tabs in Roojs
41370          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41371          */
41372         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41373         //this.el.endMeasure();
41374     //},
41375
41376     /**
41377      * Sets the text for the tab (Note: this also sets the tooltip text)
41378      * @param {String} text The tab's text and tooltip
41379      */
41380     setText : function(text){
41381         this.text = text;
41382         this.textEl.update(text);
41383         this.setTooltip(text);
41384         //if(!this.tabPanel.resizeTabs){
41385         //    this.autoSize();
41386         //}
41387     },
41388     /**
41389      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41390      */
41391     activate : function(){
41392         this.tabPanel.activate(this.id);
41393     },
41394
41395     /**
41396      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41397      */
41398     disable : function(){
41399         if(this.tabPanel.active != this){
41400             this.disabled = true;
41401             this.status_node.addClass("disabled");
41402         }
41403     },
41404
41405     /**
41406      * Enables this TabPanelItem if it was previously disabled.
41407      */
41408     enable : function(){
41409         this.disabled = false;
41410         this.status_node.removeClass("disabled");
41411     },
41412
41413     /**
41414      * Sets the content for this TabPanelItem.
41415      * @param {String} content The content
41416      * @param {Boolean} loadScripts true to look for and load scripts
41417      */
41418     setContent : function(content, loadScripts){
41419         this.bodyEl.update(content, loadScripts);
41420     },
41421
41422     /**
41423      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41424      * @return {Roo.UpdateManager} The UpdateManager
41425      */
41426     getUpdateManager : function(){
41427         return this.bodyEl.getUpdateManager();
41428     },
41429
41430     /**
41431      * Set a URL to be used to load the content for this TabPanelItem.
41432      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41433      * @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)
41434      * @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)
41435      * @return {Roo.UpdateManager} The UpdateManager
41436      */
41437     setUrl : function(url, params, loadOnce){
41438         if(this.refreshDelegate){
41439             this.un('activate', this.refreshDelegate);
41440         }
41441         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41442         this.on("activate", this.refreshDelegate);
41443         return this.bodyEl.getUpdateManager();
41444     },
41445
41446     /** @private */
41447     _handleRefresh : function(url, params, loadOnce){
41448         if(!loadOnce || !this.loaded){
41449             var updater = this.bodyEl.getUpdateManager();
41450             updater.update(url, params, this._setLoaded.createDelegate(this));
41451         }
41452     },
41453
41454     /**
41455      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41456      *   Will fail silently if the setUrl method has not been called.
41457      *   This does not activate the panel, just updates its content.
41458      */
41459     refresh : function(){
41460         if(this.refreshDelegate){
41461            this.loaded = false;
41462            this.refreshDelegate();
41463         }
41464     },
41465
41466     /** @private */
41467     _setLoaded : function(){
41468         this.loaded = true;
41469     },
41470
41471     /** @private */
41472     closeClick : function(e){
41473         var o = {};
41474         e.stopEvent();
41475         this.fireEvent("beforeclose", this, o);
41476         if(o.cancel !== true){
41477             this.tabPanel.removeTab(this.id);
41478         }
41479     },
41480     /**
41481      * The text displayed in the tooltip for the close icon.
41482      * @type String
41483      */
41484     closeText : "Close this tab"
41485 });
41486 /**
41487 *    This script refer to:
41488 *    Title: International Telephone Input
41489 *    Author: Jack O'Connor
41490 *    Code version:  v12.1.12
41491 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41492 **/
41493
41494 Roo.bootstrap.PhoneInputData = function() {
41495     var d = [
41496       [
41497         "Afghanistan (‫افغانستان‬‎)",
41498         "af",
41499         "93"
41500       ],
41501       [
41502         "Albania (Shqipëri)",
41503         "al",
41504         "355"
41505       ],
41506       [
41507         "Algeria (‫الجزائر‬‎)",
41508         "dz",
41509         "213"
41510       ],
41511       [
41512         "American Samoa",
41513         "as",
41514         "1684"
41515       ],
41516       [
41517         "Andorra",
41518         "ad",
41519         "376"
41520       ],
41521       [
41522         "Angola",
41523         "ao",
41524         "244"
41525       ],
41526       [
41527         "Anguilla",
41528         "ai",
41529         "1264"
41530       ],
41531       [
41532         "Antigua and Barbuda",
41533         "ag",
41534         "1268"
41535       ],
41536       [
41537         "Argentina",
41538         "ar",
41539         "54"
41540       ],
41541       [
41542         "Armenia (Հայաստան)",
41543         "am",
41544         "374"
41545       ],
41546       [
41547         "Aruba",
41548         "aw",
41549         "297"
41550       ],
41551       [
41552         "Australia",
41553         "au",
41554         "61",
41555         0
41556       ],
41557       [
41558         "Austria (Österreich)",
41559         "at",
41560         "43"
41561       ],
41562       [
41563         "Azerbaijan (Azərbaycan)",
41564         "az",
41565         "994"
41566       ],
41567       [
41568         "Bahamas",
41569         "bs",
41570         "1242"
41571       ],
41572       [
41573         "Bahrain (‫البحرين‬‎)",
41574         "bh",
41575         "973"
41576       ],
41577       [
41578         "Bangladesh (বাংলাদেশ)",
41579         "bd",
41580         "880"
41581       ],
41582       [
41583         "Barbados",
41584         "bb",
41585         "1246"
41586       ],
41587       [
41588         "Belarus (Беларусь)",
41589         "by",
41590         "375"
41591       ],
41592       [
41593         "Belgium (België)",
41594         "be",
41595         "32"
41596       ],
41597       [
41598         "Belize",
41599         "bz",
41600         "501"
41601       ],
41602       [
41603         "Benin (Bénin)",
41604         "bj",
41605         "229"
41606       ],
41607       [
41608         "Bermuda",
41609         "bm",
41610         "1441"
41611       ],
41612       [
41613         "Bhutan (འབྲུག)",
41614         "bt",
41615         "975"
41616       ],
41617       [
41618         "Bolivia",
41619         "bo",
41620         "591"
41621       ],
41622       [
41623         "Bosnia and Herzegovina (Босна и Херцеговина)",
41624         "ba",
41625         "387"
41626       ],
41627       [
41628         "Botswana",
41629         "bw",
41630         "267"
41631       ],
41632       [
41633         "Brazil (Brasil)",
41634         "br",
41635         "55"
41636       ],
41637       [
41638         "British Indian Ocean Territory",
41639         "io",
41640         "246"
41641       ],
41642       [
41643         "British Virgin Islands",
41644         "vg",
41645         "1284"
41646       ],
41647       [
41648         "Brunei",
41649         "bn",
41650         "673"
41651       ],
41652       [
41653         "Bulgaria (България)",
41654         "bg",
41655         "359"
41656       ],
41657       [
41658         "Burkina Faso",
41659         "bf",
41660         "226"
41661       ],
41662       [
41663         "Burundi (Uburundi)",
41664         "bi",
41665         "257"
41666       ],
41667       [
41668         "Cambodia (កម្ពុជា)",
41669         "kh",
41670         "855"
41671       ],
41672       [
41673         "Cameroon (Cameroun)",
41674         "cm",
41675         "237"
41676       ],
41677       [
41678         "Canada",
41679         "ca",
41680         "1",
41681         1,
41682         ["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"]
41683       ],
41684       [
41685         "Cape Verde (Kabu Verdi)",
41686         "cv",
41687         "238"
41688       ],
41689       [
41690         "Caribbean Netherlands",
41691         "bq",
41692         "599",
41693         1
41694       ],
41695       [
41696         "Cayman Islands",
41697         "ky",
41698         "1345"
41699       ],
41700       [
41701         "Central African Republic (République centrafricaine)",
41702         "cf",
41703         "236"
41704       ],
41705       [
41706         "Chad (Tchad)",
41707         "td",
41708         "235"
41709       ],
41710       [
41711         "Chile",
41712         "cl",
41713         "56"
41714       ],
41715       [
41716         "China (中国)",
41717         "cn",
41718         "86"
41719       ],
41720       [
41721         "Christmas Island",
41722         "cx",
41723         "61",
41724         2
41725       ],
41726       [
41727         "Cocos (Keeling) Islands",
41728         "cc",
41729         "61",
41730         1
41731       ],
41732       [
41733         "Colombia",
41734         "co",
41735         "57"
41736       ],
41737       [
41738         "Comoros (‫جزر القمر‬‎)",
41739         "km",
41740         "269"
41741       ],
41742       [
41743         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41744         "cd",
41745         "243"
41746       ],
41747       [
41748         "Congo (Republic) (Congo-Brazzaville)",
41749         "cg",
41750         "242"
41751       ],
41752       [
41753         "Cook Islands",
41754         "ck",
41755         "682"
41756       ],
41757       [
41758         "Costa Rica",
41759         "cr",
41760         "506"
41761       ],
41762       [
41763         "Côte d’Ivoire",
41764         "ci",
41765         "225"
41766       ],
41767       [
41768         "Croatia (Hrvatska)",
41769         "hr",
41770         "385"
41771       ],
41772       [
41773         "Cuba",
41774         "cu",
41775         "53"
41776       ],
41777       [
41778         "Curaçao",
41779         "cw",
41780         "599",
41781         0
41782       ],
41783       [
41784         "Cyprus (Κύπρος)",
41785         "cy",
41786         "357"
41787       ],
41788       [
41789         "Czech Republic (Česká republika)",
41790         "cz",
41791         "420"
41792       ],
41793       [
41794         "Denmark (Danmark)",
41795         "dk",
41796         "45"
41797       ],
41798       [
41799         "Djibouti",
41800         "dj",
41801         "253"
41802       ],
41803       [
41804         "Dominica",
41805         "dm",
41806         "1767"
41807       ],
41808       [
41809         "Dominican Republic (República Dominicana)",
41810         "do",
41811         "1",
41812         2,
41813         ["809", "829", "849"]
41814       ],
41815       [
41816         "Ecuador",
41817         "ec",
41818         "593"
41819       ],
41820       [
41821         "Egypt (‫مصر‬‎)",
41822         "eg",
41823         "20"
41824       ],
41825       [
41826         "El Salvador",
41827         "sv",
41828         "503"
41829       ],
41830       [
41831         "Equatorial Guinea (Guinea Ecuatorial)",
41832         "gq",
41833         "240"
41834       ],
41835       [
41836         "Eritrea",
41837         "er",
41838         "291"
41839       ],
41840       [
41841         "Estonia (Eesti)",
41842         "ee",
41843         "372"
41844       ],
41845       [
41846         "Ethiopia",
41847         "et",
41848         "251"
41849       ],
41850       [
41851         "Falkland Islands (Islas Malvinas)",
41852         "fk",
41853         "500"
41854       ],
41855       [
41856         "Faroe Islands (Føroyar)",
41857         "fo",
41858         "298"
41859       ],
41860       [
41861         "Fiji",
41862         "fj",
41863         "679"
41864       ],
41865       [
41866         "Finland (Suomi)",
41867         "fi",
41868         "358",
41869         0
41870       ],
41871       [
41872         "France",
41873         "fr",
41874         "33"
41875       ],
41876       [
41877         "French Guiana (Guyane française)",
41878         "gf",
41879         "594"
41880       ],
41881       [
41882         "French Polynesia (Polynésie française)",
41883         "pf",
41884         "689"
41885       ],
41886       [
41887         "Gabon",
41888         "ga",
41889         "241"
41890       ],
41891       [
41892         "Gambia",
41893         "gm",
41894         "220"
41895       ],
41896       [
41897         "Georgia (საქართველო)",
41898         "ge",
41899         "995"
41900       ],
41901       [
41902         "Germany (Deutschland)",
41903         "de",
41904         "49"
41905       ],
41906       [
41907         "Ghana (Gaana)",
41908         "gh",
41909         "233"
41910       ],
41911       [
41912         "Gibraltar",
41913         "gi",
41914         "350"
41915       ],
41916       [
41917         "Greece (Ελλάδα)",
41918         "gr",
41919         "30"
41920       ],
41921       [
41922         "Greenland (Kalaallit Nunaat)",
41923         "gl",
41924         "299"
41925       ],
41926       [
41927         "Grenada",
41928         "gd",
41929         "1473"
41930       ],
41931       [
41932         "Guadeloupe",
41933         "gp",
41934         "590",
41935         0
41936       ],
41937       [
41938         "Guam",
41939         "gu",
41940         "1671"
41941       ],
41942       [
41943         "Guatemala",
41944         "gt",
41945         "502"
41946       ],
41947       [
41948         "Guernsey",
41949         "gg",
41950         "44",
41951         1
41952       ],
41953       [
41954         "Guinea (Guinée)",
41955         "gn",
41956         "224"
41957       ],
41958       [
41959         "Guinea-Bissau (Guiné Bissau)",
41960         "gw",
41961         "245"
41962       ],
41963       [
41964         "Guyana",
41965         "gy",
41966         "592"
41967       ],
41968       [
41969         "Haiti",
41970         "ht",
41971         "509"
41972       ],
41973       [
41974         "Honduras",
41975         "hn",
41976         "504"
41977       ],
41978       [
41979         "Hong Kong (香港)",
41980         "hk",
41981         "852"
41982       ],
41983       [
41984         "Hungary (Magyarország)",
41985         "hu",
41986         "36"
41987       ],
41988       [
41989         "Iceland (Ísland)",
41990         "is",
41991         "354"
41992       ],
41993       [
41994         "India (भारत)",
41995         "in",
41996         "91"
41997       ],
41998       [
41999         "Indonesia",
42000         "id",
42001         "62"
42002       ],
42003       [
42004         "Iran (‫ایران‬‎)",
42005         "ir",
42006         "98"
42007       ],
42008       [
42009         "Iraq (‫العراق‬‎)",
42010         "iq",
42011         "964"
42012       ],
42013       [
42014         "Ireland",
42015         "ie",
42016         "353"
42017       ],
42018       [
42019         "Isle of Man",
42020         "im",
42021         "44",
42022         2
42023       ],
42024       [
42025         "Israel (‫ישראל‬‎)",
42026         "il",
42027         "972"
42028       ],
42029       [
42030         "Italy (Italia)",
42031         "it",
42032         "39",
42033         0
42034       ],
42035       [
42036         "Jamaica",
42037         "jm",
42038         "1876"
42039       ],
42040       [
42041         "Japan (日本)",
42042         "jp",
42043         "81"
42044       ],
42045       [
42046         "Jersey",
42047         "je",
42048         "44",
42049         3
42050       ],
42051       [
42052         "Jordan (‫الأردن‬‎)",
42053         "jo",
42054         "962"
42055       ],
42056       [
42057         "Kazakhstan (Казахстан)",
42058         "kz",
42059         "7",
42060         1
42061       ],
42062       [
42063         "Kenya",
42064         "ke",
42065         "254"
42066       ],
42067       [
42068         "Kiribati",
42069         "ki",
42070         "686"
42071       ],
42072       [
42073         "Kosovo",
42074         "xk",
42075         "383"
42076       ],
42077       [
42078         "Kuwait (‫الكويت‬‎)",
42079         "kw",
42080         "965"
42081       ],
42082       [
42083         "Kyrgyzstan (Кыргызстан)",
42084         "kg",
42085         "996"
42086       ],
42087       [
42088         "Laos (ລາວ)",
42089         "la",
42090         "856"
42091       ],
42092       [
42093         "Latvia (Latvija)",
42094         "lv",
42095         "371"
42096       ],
42097       [
42098         "Lebanon (‫لبنان‬‎)",
42099         "lb",
42100         "961"
42101       ],
42102       [
42103         "Lesotho",
42104         "ls",
42105         "266"
42106       ],
42107       [
42108         "Liberia",
42109         "lr",
42110         "231"
42111       ],
42112       [
42113         "Libya (‫ليبيا‬‎)",
42114         "ly",
42115         "218"
42116       ],
42117       [
42118         "Liechtenstein",
42119         "li",
42120         "423"
42121       ],
42122       [
42123         "Lithuania (Lietuva)",
42124         "lt",
42125         "370"
42126       ],
42127       [
42128         "Luxembourg",
42129         "lu",
42130         "352"
42131       ],
42132       [
42133         "Macau (澳門)",
42134         "mo",
42135         "853"
42136       ],
42137       [
42138         "Macedonia (FYROM) (Македонија)",
42139         "mk",
42140         "389"
42141       ],
42142       [
42143         "Madagascar (Madagasikara)",
42144         "mg",
42145         "261"
42146       ],
42147       [
42148         "Malawi",
42149         "mw",
42150         "265"
42151       ],
42152       [
42153         "Malaysia",
42154         "my",
42155         "60"
42156       ],
42157       [
42158         "Maldives",
42159         "mv",
42160         "960"
42161       ],
42162       [
42163         "Mali",
42164         "ml",
42165         "223"
42166       ],
42167       [
42168         "Malta",
42169         "mt",
42170         "356"
42171       ],
42172       [
42173         "Marshall Islands",
42174         "mh",
42175         "692"
42176       ],
42177       [
42178         "Martinique",
42179         "mq",
42180         "596"
42181       ],
42182       [
42183         "Mauritania (‫موريتانيا‬‎)",
42184         "mr",
42185         "222"
42186       ],
42187       [
42188         "Mauritius (Moris)",
42189         "mu",
42190         "230"
42191       ],
42192       [
42193         "Mayotte",
42194         "yt",
42195         "262",
42196         1
42197       ],
42198       [
42199         "Mexico (México)",
42200         "mx",
42201         "52"
42202       ],
42203       [
42204         "Micronesia",
42205         "fm",
42206         "691"
42207       ],
42208       [
42209         "Moldova (Republica Moldova)",
42210         "md",
42211         "373"
42212       ],
42213       [
42214         "Monaco",
42215         "mc",
42216         "377"
42217       ],
42218       [
42219         "Mongolia (Монгол)",
42220         "mn",
42221         "976"
42222       ],
42223       [
42224         "Montenegro (Crna Gora)",
42225         "me",
42226         "382"
42227       ],
42228       [
42229         "Montserrat",
42230         "ms",
42231         "1664"
42232       ],
42233       [
42234         "Morocco (‫المغرب‬‎)",
42235         "ma",
42236         "212",
42237         0
42238       ],
42239       [
42240         "Mozambique (Moçambique)",
42241         "mz",
42242         "258"
42243       ],
42244       [
42245         "Myanmar (Burma) (မြန်မာ)",
42246         "mm",
42247         "95"
42248       ],
42249       [
42250         "Namibia (Namibië)",
42251         "na",
42252         "264"
42253       ],
42254       [
42255         "Nauru",
42256         "nr",
42257         "674"
42258       ],
42259       [
42260         "Nepal (नेपाल)",
42261         "np",
42262         "977"
42263       ],
42264       [
42265         "Netherlands (Nederland)",
42266         "nl",
42267         "31"
42268       ],
42269       [
42270         "New Caledonia (Nouvelle-Calédonie)",
42271         "nc",
42272         "687"
42273       ],
42274       [
42275         "New Zealand",
42276         "nz",
42277         "64"
42278       ],
42279       [
42280         "Nicaragua",
42281         "ni",
42282         "505"
42283       ],
42284       [
42285         "Niger (Nijar)",
42286         "ne",
42287         "227"
42288       ],
42289       [
42290         "Nigeria",
42291         "ng",
42292         "234"
42293       ],
42294       [
42295         "Niue",
42296         "nu",
42297         "683"
42298       ],
42299       [
42300         "Norfolk Island",
42301         "nf",
42302         "672"
42303       ],
42304       [
42305         "North Korea (조선 민주주의 인민 공화국)",
42306         "kp",
42307         "850"
42308       ],
42309       [
42310         "Northern Mariana Islands",
42311         "mp",
42312         "1670"
42313       ],
42314       [
42315         "Norway (Norge)",
42316         "no",
42317         "47",
42318         0
42319       ],
42320       [
42321         "Oman (‫عُمان‬‎)",
42322         "om",
42323         "968"
42324       ],
42325       [
42326         "Pakistan (‫پاکستان‬‎)",
42327         "pk",
42328         "92"
42329       ],
42330       [
42331         "Palau",
42332         "pw",
42333         "680"
42334       ],
42335       [
42336         "Palestine (‫فلسطين‬‎)",
42337         "ps",
42338         "970"
42339       ],
42340       [
42341         "Panama (Panamá)",
42342         "pa",
42343         "507"
42344       ],
42345       [
42346         "Papua New Guinea",
42347         "pg",
42348         "675"
42349       ],
42350       [
42351         "Paraguay",
42352         "py",
42353         "595"
42354       ],
42355       [
42356         "Peru (Perú)",
42357         "pe",
42358         "51"
42359       ],
42360       [
42361         "Philippines",
42362         "ph",
42363         "63"
42364       ],
42365       [
42366         "Poland (Polska)",
42367         "pl",
42368         "48"
42369       ],
42370       [
42371         "Portugal",
42372         "pt",
42373         "351"
42374       ],
42375       [
42376         "Puerto Rico",
42377         "pr",
42378         "1",
42379         3,
42380         ["787", "939"]
42381       ],
42382       [
42383         "Qatar (‫قطر‬‎)",
42384         "qa",
42385         "974"
42386       ],
42387       [
42388         "Réunion (La Réunion)",
42389         "re",
42390         "262",
42391         0
42392       ],
42393       [
42394         "Romania (România)",
42395         "ro",
42396         "40"
42397       ],
42398       [
42399         "Russia (Россия)",
42400         "ru",
42401         "7",
42402         0
42403       ],
42404       [
42405         "Rwanda",
42406         "rw",
42407         "250"
42408       ],
42409       [
42410         "Saint Barthélemy",
42411         "bl",
42412         "590",
42413         1
42414       ],
42415       [
42416         "Saint Helena",
42417         "sh",
42418         "290"
42419       ],
42420       [
42421         "Saint Kitts and Nevis",
42422         "kn",
42423         "1869"
42424       ],
42425       [
42426         "Saint Lucia",
42427         "lc",
42428         "1758"
42429       ],
42430       [
42431         "Saint Martin (Saint-Martin (partie française))",
42432         "mf",
42433         "590",
42434         2
42435       ],
42436       [
42437         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42438         "pm",
42439         "508"
42440       ],
42441       [
42442         "Saint Vincent and the Grenadines",
42443         "vc",
42444         "1784"
42445       ],
42446       [
42447         "Samoa",
42448         "ws",
42449         "685"
42450       ],
42451       [
42452         "San Marino",
42453         "sm",
42454         "378"
42455       ],
42456       [
42457         "São Tomé and Príncipe (São Tomé e Príncipe)",
42458         "st",
42459         "239"
42460       ],
42461       [
42462         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42463         "sa",
42464         "966"
42465       ],
42466       [
42467         "Senegal (Sénégal)",
42468         "sn",
42469         "221"
42470       ],
42471       [
42472         "Serbia (Србија)",
42473         "rs",
42474         "381"
42475       ],
42476       [
42477         "Seychelles",
42478         "sc",
42479         "248"
42480       ],
42481       [
42482         "Sierra Leone",
42483         "sl",
42484         "232"
42485       ],
42486       [
42487         "Singapore",
42488         "sg",
42489         "65"
42490       ],
42491       [
42492         "Sint Maarten",
42493         "sx",
42494         "1721"
42495       ],
42496       [
42497         "Slovakia (Slovensko)",
42498         "sk",
42499         "421"
42500       ],
42501       [
42502         "Slovenia (Slovenija)",
42503         "si",
42504         "386"
42505       ],
42506       [
42507         "Solomon Islands",
42508         "sb",
42509         "677"
42510       ],
42511       [
42512         "Somalia (Soomaaliya)",
42513         "so",
42514         "252"
42515       ],
42516       [
42517         "South Africa",
42518         "za",
42519         "27"
42520       ],
42521       [
42522         "South Korea (대한민국)",
42523         "kr",
42524         "82"
42525       ],
42526       [
42527         "South Sudan (‫جنوب السودان‬‎)",
42528         "ss",
42529         "211"
42530       ],
42531       [
42532         "Spain (España)",
42533         "es",
42534         "34"
42535       ],
42536       [
42537         "Sri Lanka (ශ්‍රී ලංකාව)",
42538         "lk",
42539         "94"
42540       ],
42541       [
42542         "Sudan (‫السودان‬‎)",
42543         "sd",
42544         "249"
42545       ],
42546       [
42547         "Suriname",
42548         "sr",
42549         "597"
42550       ],
42551       [
42552         "Svalbard and Jan Mayen",
42553         "sj",
42554         "47",
42555         1
42556       ],
42557       [
42558         "Swaziland",
42559         "sz",
42560         "268"
42561       ],
42562       [
42563         "Sweden (Sverige)",
42564         "se",
42565         "46"
42566       ],
42567       [
42568         "Switzerland (Schweiz)",
42569         "ch",
42570         "41"
42571       ],
42572       [
42573         "Syria (‫سوريا‬‎)",
42574         "sy",
42575         "963"
42576       ],
42577       [
42578         "Taiwan (台灣)",
42579         "tw",
42580         "886"
42581       ],
42582       [
42583         "Tajikistan",
42584         "tj",
42585         "992"
42586       ],
42587       [
42588         "Tanzania",
42589         "tz",
42590         "255"
42591       ],
42592       [
42593         "Thailand (ไทย)",
42594         "th",
42595         "66"
42596       ],
42597       [
42598         "Timor-Leste",
42599         "tl",
42600         "670"
42601       ],
42602       [
42603         "Togo",
42604         "tg",
42605         "228"
42606       ],
42607       [
42608         "Tokelau",
42609         "tk",
42610         "690"
42611       ],
42612       [
42613         "Tonga",
42614         "to",
42615         "676"
42616       ],
42617       [
42618         "Trinidad and Tobago",
42619         "tt",
42620         "1868"
42621       ],
42622       [
42623         "Tunisia (‫تونس‬‎)",
42624         "tn",
42625         "216"
42626       ],
42627       [
42628         "Turkey (Türkiye)",
42629         "tr",
42630         "90"
42631       ],
42632       [
42633         "Turkmenistan",
42634         "tm",
42635         "993"
42636       ],
42637       [
42638         "Turks and Caicos Islands",
42639         "tc",
42640         "1649"
42641       ],
42642       [
42643         "Tuvalu",
42644         "tv",
42645         "688"
42646       ],
42647       [
42648         "U.S. Virgin Islands",
42649         "vi",
42650         "1340"
42651       ],
42652       [
42653         "Uganda",
42654         "ug",
42655         "256"
42656       ],
42657       [
42658         "Ukraine (Україна)",
42659         "ua",
42660         "380"
42661       ],
42662       [
42663         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42664         "ae",
42665         "971"
42666       ],
42667       [
42668         "United Kingdom",
42669         "gb",
42670         "44",
42671         0
42672       ],
42673       [
42674         "United States",
42675         "us",
42676         "1",
42677         0
42678       ],
42679       [
42680         "Uruguay",
42681         "uy",
42682         "598"
42683       ],
42684       [
42685         "Uzbekistan (Oʻzbekiston)",
42686         "uz",
42687         "998"
42688       ],
42689       [
42690         "Vanuatu",
42691         "vu",
42692         "678"
42693       ],
42694       [
42695         "Vatican City (Città del Vaticano)",
42696         "va",
42697         "39",
42698         1
42699       ],
42700       [
42701         "Venezuela",
42702         "ve",
42703         "58"
42704       ],
42705       [
42706         "Vietnam (Việt Nam)",
42707         "vn",
42708         "84"
42709       ],
42710       [
42711         "Wallis and Futuna (Wallis-et-Futuna)",
42712         "wf",
42713         "681"
42714       ],
42715       [
42716         "Western Sahara (‫الصحراء الغربية‬‎)",
42717         "eh",
42718         "212",
42719         1
42720       ],
42721       [
42722         "Yemen (‫اليمن‬‎)",
42723         "ye",
42724         "967"
42725       ],
42726       [
42727         "Zambia",
42728         "zm",
42729         "260"
42730       ],
42731       [
42732         "Zimbabwe",
42733         "zw",
42734         "263"
42735       ],
42736       [
42737         "Åland Islands",
42738         "ax",
42739         "358",
42740         1
42741       ]
42742   ];
42743   
42744   return d;
42745 }/**
42746 *    This script refer to:
42747 *    Title: International Telephone Input
42748 *    Author: Jack O'Connor
42749 *    Code version:  v12.1.12
42750 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42751 **/
42752
42753 /**
42754  * @class Roo.bootstrap.PhoneInput
42755  * @extends Roo.bootstrap.TriggerField
42756  * An input with International dial-code selection
42757  
42758  * @cfg {String} defaultDialCode default '+852'
42759  * @cfg {Array} preferedCountries default []
42760   
42761  * @constructor
42762  * Create a new PhoneInput.
42763  * @param {Object} config Configuration options
42764  */
42765
42766 Roo.bootstrap.PhoneInput = function(config) {
42767     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42768 };
42769
42770 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42771         
42772         listWidth: undefined,
42773         
42774         selectedClass: 'active',
42775         
42776         invalidClass : "has-warning",
42777         
42778         validClass: 'has-success',
42779         
42780         allowed: '0123456789',
42781         
42782         max_length: 15,
42783         
42784         /**
42785          * @cfg {String} defaultDialCode The default dial code when initializing the input
42786          */
42787         defaultDialCode: '+852',
42788         
42789         /**
42790          * @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
42791          */
42792         preferedCountries: false,
42793         
42794         getAutoCreate : function()
42795         {
42796             var data = Roo.bootstrap.PhoneInputData();
42797             var align = this.labelAlign || this.parentLabelAlign();
42798             var id = Roo.id();
42799             
42800             this.allCountries = [];
42801             this.dialCodeMapping = [];
42802             
42803             for (var i = 0; i < data.length; i++) {
42804               var c = data[i];
42805               this.allCountries[i] = {
42806                 name: c[0],
42807                 iso2: c[1],
42808                 dialCode: c[2],
42809                 priority: c[3] || 0,
42810                 areaCodes: c[4] || null
42811               };
42812               this.dialCodeMapping[c[2]] = {
42813                   name: c[0],
42814                   iso2: c[1],
42815                   priority: c[3] || 0,
42816                   areaCodes: c[4] || null
42817               };
42818             }
42819             
42820             var cfg = {
42821                 cls: 'form-group',
42822                 cn: []
42823             };
42824             
42825             var input =  {
42826                 tag: 'input',
42827                 id : id,
42828                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42829                 maxlength: this.max_length,
42830                 cls : 'form-control tel-input',
42831                 autocomplete: 'new-password'
42832             };
42833             
42834             var hiddenInput = {
42835                 tag: 'input',
42836                 type: 'hidden',
42837                 cls: 'hidden-tel-input'
42838             };
42839             
42840             if (this.name) {
42841                 hiddenInput.name = this.name;
42842             }
42843             
42844             if (this.disabled) {
42845                 input.disabled = true;
42846             }
42847             
42848             var flag_container = {
42849                 tag: 'div',
42850                 cls: 'flag-box',
42851                 cn: [
42852                     {
42853                         tag: 'div',
42854                         cls: 'flag'
42855                     },
42856                     {
42857                         tag: 'div',
42858                         cls: 'caret'
42859                     }
42860                 ]
42861             };
42862             
42863             var box = {
42864                 tag: 'div',
42865                 cls: this.hasFeedback ? 'has-feedback' : '',
42866                 cn: [
42867                     hiddenInput,
42868                     input,
42869                     {
42870                         tag: 'input',
42871                         cls: 'dial-code-holder',
42872                         disabled: true
42873                     }
42874                 ]
42875             };
42876             
42877             var container = {
42878                 cls: 'roo-select2-container input-group',
42879                 cn: [
42880                     flag_container,
42881                     box
42882                 ]
42883             };
42884             
42885             if (this.fieldLabel.length) {
42886                 var indicator = {
42887                     tag: 'i',
42888                     tooltip: 'This field is required'
42889                 };
42890                 
42891                 var label = {
42892                     tag: 'label',
42893                     'for':  id,
42894                     cls: 'control-label',
42895                     cn: []
42896                 };
42897                 
42898                 var label_text = {
42899                     tag: 'span',
42900                     html: this.fieldLabel
42901                 };
42902                 
42903                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42904                 label.cn = [
42905                     indicator,
42906                     label_text
42907                 ];
42908                 
42909                 if(this.indicatorpos == 'right') {
42910                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42911                     label.cn = [
42912                         label_text,
42913                         indicator
42914                     ];
42915                 }
42916                 
42917                 if(align == 'left') {
42918                     container = {
42919                         tag: 'div',
42920                         cn: [
42921                             container
42922                         ]
42923                     };
42924                     
42925                     if(this.labelWidth > 12){
42926                         label.style = "width: " + this.labelWidth + 'px';
42927                     }
42928                     if(this.labelWidth < 13 && this.labelmd == 0){
42929                         this.labelmd = this.labelWidth;
42930                     }
42931                     if(this.labellg > 0){
42932                         label.cls += ' col-lg-' + this.labellg;
42933                         input.cls += ' col-lg-' + (12 - this.labellg);
42934                     }
42935                     if(this.labelmd > 0){
42936                         label.cls += ' col-md-' + this.labelmd;
42937                         container.cls += ' col-md-' + (12 - this.labelmd);
42938                     }
42939                     if(this.labelsm > 0){
42940                         label.cls += ' col-sm-' + this.labelsm;
42941                         container.cls += ' col-sm-' + (12 - this.labelsm);
42942                     }
42943                     if(this.labelxs > 0){
42944                         label.cls += ' col-xs-' + this.labelxs;
42945                         container.cls += ' col-xs-' + (12 - this.labelxs);
42946                     }
42947                 }
42948             }
42949             
42950             cfg.cn = [
42951                 label,
42952                 container
42953             ];
42954             
42955             var settings = this;
42956             
42957             ['xs','sm','md','lg'].map(function(size){
42958                 if (settings[size]) {
42959                     cfg.cls += ' col-' + size + '-' + settings[size];
42960                 }
42961             });
42962             
42963             this.store = new Roo.data.Store({
42964                 proxy : new Roo.data.MemoryProxy({}),
42965                 reader : new Roo.data.JsonReader({
42966                     fields : [
42967                         {
42968                             'name' : 'name',
42969                             'type' : 'string'
42970                         },
42971                         {
42972                             'name' : 'iso2',
42973                             'type' : 'string'
42974                         },
42975                         {
42976                             'name' : 'dialCode',
42977                             'type' : 'string'
42978                         },
42979                         {
42980                             'name' : 'priority',
42981                             'type' : 'string'
42982                         },
42983                         {
42984                             'name' : 'areaCodes',
42985                             'type' : 'string'
42986                         }
42987                     ]
42988                 })
42989             });
42990             
42991             if(!this.preferedCountries) {
42992                 this.preferedCountries = [
42993                     'hk',
42994                     'gb',
42995                     'us'
42996                 ];
42997             }
42998             
42999             var p = this.preferedCountries.reverse();
43000             
43001             if(p) {
43002                 for (var i = 0; i < p.length; i++) {
43003                     for (var j = 0; j < this.allCountries.length; j++) {
43004                         if(this.allCountries[j].iso2 == p[i]) {
43005                             var t = this.allCountries[j];
43006                             this.allCountries.splice(j,1);
43007                             this.allCountries.unshift(t);
43008                         }
43009                     } 
43010                 }
43011             }
43012             
43013             this.store.proxy.data = {
43014                 success: true,
43015                 data: this.allCountries
43016             };
43017             
43018             return cfg;
43019         },
43020         
43021         initEvents : function()
43022         {
43023             this.createList();
43024             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43025             
43026             this.indicator = this.indicatorEl();
43027             this.flag = this.flagEl();
43028             this.dialCodeHolder = this.dialCodeHolderEl();
43029             
43030             this.trigger = this.el.select('div.flag-box',true).first();
43031             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43032             
43033             var _this = this;
43034             
43035             (function(){
43036                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43037                 _this.list.setWidth(lw);
43038             }).defer(100);
43039             
43040             this.list.on('mouseover', this.onViewOver, this);
43041             this.list.on('mousemove', this.onViewMove, this);
43042             this.inputEl().on("keyup", this.onKeyUp, this);
43043             this.inputEl().on("keypress", this.onKeyPress, this);
43044             
43045             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43046
43047             this.view = new Roo.View(this.list, this.tpl, {
43048                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43049             });
43050             
43051             this.view.on('click', this.onViewClick, this);
43052             this.setValue(this.defaultDialCode);
43053         },
43054         
43055         onTriggerClick : function(e)
43056         {
43057             Roo.log('trigger click');
43058             if(this.disabled){
43059                 return;
43060             }
43061             
43062             if(this.isExpanded()){
43063                 this.collapse();
43064                 this.hasFocus = false;
43065             }else {
43066                 this.store.load({});
43067                 this.hasFocus = true;
43068                 this.expand();
43069             }
43070         },
43071         
43072         isExpanded : function()
43073         {
43074             return this.list.isVisible();
43075         },
43076         
43077         collapse : function()
43078         {
43079             if(!this.isExpanded()){
43080                 return;
43081             }
43082             this.list.hide();
43083             Roo.get(document).un('mousedown', this.collapseIf, this);
43084             Roo.get(document).un('mousewheel', this.collapseIf, this);
43085             this.fireEvent('collapse', this);
43086             this.validate();
43087         },
43088         
43089         expand : function()
43090         {
43091             Roo.log('expand');
43092
43093             if(this.isExpanded() || !this.hasFocus){
43094                 return;
43095             }
43096             
43097             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43098             this.list.setWidth(lw);
43099             
43100             this.list.show();
43101             this.restrictHeight();
43102             
43103             Roo.get(document).on('mousedown', this.collapseIf, this);
43104             Roo.get(document).on('mousewheel', this.collapseIf, this);
43105             
43106             this.fireEvent('expand', this);
43107         },
43108         
43109         restrictHeight : function()
43110         {
43111             this.list.alignTo(this.inputEl(), this.listAlign);
43112             this.list.alignTo(this.inputEl(), this.listAlign);
43113         },
43114         
43115         onViewOver : function(e, t)
43116         {
43117             if(this.inKeyMode){
43118                 return;
43119             }
43120             var item = this.view.findItemFromChild(t);
43121             
43122             if(item){
43123                 var index = this.view.indexOf(item);
43124                 this.select(index, false);
43125             }
43126         },
43127
43128         // private
43129         onViewClick : function(view, doFocus, el, e)
43130         {
43131             var index = this.view.getSelectedIndexes()[0];
43132             
43133             var r = this.store.getAt(index);
43134             
43135             if(r){
43136                 this.onSelect(r, index);
43137             }
43138             if(doFocus !== false && !this.blockFocus){
43139                 this.inputEl().focus();
43140             }
43141         },
43142         
43143         onViewMove : function(e, t)
43144         {
43145             this.inKeyMode = false;
43146         },
43147         
43148         select : function(index, scrollIntoView)
43149         {
43150             this.selectedIndex = index;
43151             this.view.select(index);
43152             if(scrollIntoView !== false){
43153                 var el = this.view.getNode(index);
43154                 if(el){
43155                     this.list.scrollChildIntoView(el, false);
43156                 }
43157             }
43158         },
43159         
43160         createList : function()
43161         {
43162             this.list = Roo.get(document.body).createChild({
43163                 tag: 'ul',
43164                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43165                 style: 'display:none'
43166             });
43167             
43168             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43169         },
43170         
43171         collapseIf : function(e)
43172         {
43173             var in_combo  = e.within(this.el);
43174             var in_list =  e.within(this.list);
43175             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43176             
43177             if (in_combo || in_list || is_list) {
43178                 return;
43179             }
43180             this.collapse();
43181         },
43182         
43183         onSelect : function(record, index)
43184         {
43185             if(this.fireEvent('beforeselect', this, record, index) !== false){
43186                 
43187                 this.setFlagClass(record.data.iso2);
43188                 this.setDialCode(record.data.dialCode);
43189                 this.hasFocus = false;
43190                 this.collapse();
43191                 this.fireEvent('select', this, record, index);
43192             }
43193         },
43194         
43195         flagEl : function()
43196         {
43197             var flag = this.el.select('div.flag',true).first();
43198             if(!flag){
43199                 return false;
43200             }
43201             return flag;
43202         },
43203         
43204         dialCodeHolderEl : function()
43205         {
43206             var d = this.el.select('input.dial-code-holder',true).first();
43207             if(!d){
43208                 return false;
43209             }
43210             return d;
43211         },
43212         
43213         setDialCode : function(v)
43214         {
43215             this.dialCodeHolder.dom.value = '+'+v;
43216         },
43217         
43218         setFlagClass : function(n)
43219         {
43220             this.flag.dom.className = 'flag '+n;
43221         },
43222         
43223         getValue : function()
43224         {
43225             var v = this.inputEl().getValue();
43226             if(this.dialCodeHolder) {
43227                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43228             }
43229             return v;
43230         },
43231         
43232         setValue : function(v)
43233         {
43234             var d = this.getDialCode(v);
43235             
43236             //invalid dial code
43237             if(v.length == 0 || !d || d.length == 0) {
43238                 if(this.rendered){
43239                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43240                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43241                 }
43242                 return;
43243             }
43244             
43245             //valid dial code
43246             this.setFlagClass(this.dialCodeMapping[d].iso2);
43247             this.setDialCode(d);
43248             this.inputEl().dom.value = v.replace('+'+d,'');
43249             this.hiddenEl().dom.value = this.getValue();
43250             
43251             this.validate();
43252         },
43253         
43254         getDialCode : function(v)
43255         {
43256             v = v ||  '';
43257             
43258             if (v.length == 0) {
43259                 return this.dialCodeHolder.dom.value;
43260             }
43261             
43262             var dialCode = "";
43263             if (v.charAt(0) != "+") {
43264                 return false;
43265             }
43266             var numericChars = "";
43267             for (var i = 1; i < v.length; i++) {
43268               var c = v.charAt(i);
43269               if (!isNaN(c)) {
43270                 numericChars += c;
43271                 if (this.dialCodeMapping[numericChars]) {
43272                   dialCode = v.substr(1, i);
43273                 }
43274                 if (numericChars.length == 4) {
43275                   break;
43276                 }
43277               }
43278             }
43279             return dialCode;
43280         },
43281         
43282         reset : function()
43283         {
43284             this.setValue(this.defaultDialCode);
43285             this.validate();
43286         },
43287         
43288         hiddenEl : function()
43289         {
43290             return this.el.select('input.hidden-tel-input',true).first();
43291         },
43292         
43293         // after setting val
43294         onKeyUp : function(e){
43295             this.setValue(this.getValue());
43296         },
43297         
43298         onKeyPress : function(e){
43299             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43300                 e.stopEvent();
43301             }
43302         }
43303         
43304 });
43305 /**
43306  * @class Roo.bootstrap.MoneyField
43307  * @extends Roo.bootstrap.ComboBox
43308  * Bootstrap MoneyField class
43309  * 
43310  * @constructor
43311  * Create a new MoneyField.
43312  * @param {Object} config Configuration options
43313  */
43314
43315 Roo.bootstrap.MoneyField = function(config) {
43316     
43317     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43318     
43319 };
43320
43321 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43322     
43323     /**
43324      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43325      */
43326     allowDecimals : true,
43327     /**
43328      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43329      */
43330     decimalSeparator : ".",
43331     /**
43332      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43333      */
43334     decimalPrecision : 0,
43335     /**
43336      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43337      */
43338     allowNegative : true,
43339     /**
43340      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43341      */
43342     allowZero: true,
43343     /**
43344      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43345      */
43346     minValue : Number.NEGATIVE_INFINITY,
43347     /**
43348      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43349      */
43350     maxValue : Number.MAX_VALUE,
43351     /**
43352      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43353      */
43354     minText : "The minimum value for this field is {0}",
43355     /**
43356      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43357      */
43358     maxText : "The maximum value for this field is {0}",
43359     /**
43360      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43361      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43362      */
43363     nanText : "{0} is not a valid number",
43364     /**
43365      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43366      */
43367     castInt : true,
43368     /**
43369      * @cfg {String} defaults currency of the MoneyField
43370      * value should be in lkey
43371      */
43372     defaultCurrency : false,
43373     /**
43374      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43375      */
43376     thousandsDelimiter : false,
43377     /**
43378      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43379      */
43380     max_length: false,
43381     
43382     inputlg : 9,
43383     inputmd : 9,
43384     inputsm : 9,
43385     inputxs : 6,
43386     
43387     store : false,
43388     
43389     getAutoCreate : function()
43390     {
43391         var align = this.labelAlign || this.parentLabelAlign();
43392         
43393         var id = Roo.id();
43394
43395         var cfg = {
43396             cls: 'form-group',
43397             cn: []
43398         };
43399
43400         var input =  {
43401             tag: 'input',
43402             id : id,
43403             cls : 'form-control roo-money-amount-input',
43404             autocomplete: 'new-password'
43405         };
43406         
43407         var hiddenInput = {
43408             tag: 'input',
43409             type: 'hidden',
43410             id: Roo.id(),
43411             cls: 'hidden-number-input'
43412         };
43413         
43414         if(this.max_length) {
43415             input.maxlength = this.max_length; 
43416         }
43417         
43418         if (this.name) {
43419             hiddenInput.name = this.name;
43420         }
43421
43422         if (this.disabled) {
43423             input.disabled = true;
43424         }
43425
43426         var clg = 12 - this.inputlg;
43427         var cmd = 12 - this.inputmd;
43428         var csm = 12 - this.inputsm;
43429         var cxs = 12 - this.inputxs;
43430         
43431         var container = {
43432             tag : 'div',
43433             cls : 'row roo-money-field',
43434             cn : [
43435                 {
43436                     tag : 'div',
43437                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43438                     cn : [
43439                         {
43440                             tag : 'div',
43441                             cls: 'roo-select2-container input-group',
43442                             cn: [
43443                                 {
43444                                     tag : 'input',
43445                                     cls : 'form-control roo-money-currency-input',
43446                                     autocomplete: 'new-password',
43447                                     readOnly : 1,
43448                                     name : this.currencyName
43449                                 },
43450                                 {
43451                                     tag :'span',
43452                                     cls : 'input-group-addon',
43453                                     cn : [
43454                                         {
43455                                             tag: 'span',
43456                                             cls: 'caret'
43457                                         }
43458                                     ]
43459                                 }
43460                             ]
43461                         }
43462                     ]
43463                 },
43464                 {
43465                     tag : 'div',
43466                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43467                     cn : [
43468                         {
43469                             tag: 'div',
43470                             cls: this.hasFeedback ? 'has-feedback' : '',
43471                             cn: [
43472                                 input
43473                             ]
43474                         }
43475                     ]
43476                 }
43477             ]
43478             
43479         };
43480         
43481         if (this.fieldLabel.length) {
43482             var indicator = {
43483                 tag: 'i',
43484                 tooltip: 'This field is required'
43485             };
43486
43487             var label = {
43488                 tag: 'label',
43489                 'for':  id,
43490                 cls: 'control-label',
43491                 cn: []
43492             };
43493
43494             var label_text = {
43495                 tag: 'span',
43496                 html: this.fieldLabel
43497             };
43498
43499             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43500             label.cn = [
43501                 indicator,
43502                 label_text
43503             ];
43504
43505             if(this.indicatorpos == 'right') {
43506                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43507                 label.cn = [
43508                     label_text,
43509                     indicator
43510                 ];
43511             }
43512
43513             if(align == 'left') {
43514                 container = {
43515                     tag: 'div',
43516                     cn: [
43517                         container
43518                     ]
43519                 };
43520
43521                 if(this.labelWidth > 12){
43522                     label.style = "width: " + this.labelWidth + 'px';
43523                 }
43524                 if(this.labelWidth < 13 && this.labelmd == 0){
43525                     this.labelmd = this.labelWidth;
43526                 }
43527                 if(this.labellg > 0){
43528                     label.cls += ' col-lg-' + this.labellg;
43529                     input.cls += ' col-lg-' + (12 - this.labellg);
43530                 }
43531                 if(this.labelmd > 0){
43532                     label.cls += ' col-md-' + this.labelmd;
43533                     container.cls += ' col-md-' + (12 - this.labelmd);
43534                 }
43535                 if(this.labelsm > 0){
43536                     label.cls += ' col-sm-' + this.labelsm;
43537                     container.cls += ' col-sm-' + (12 - this.labelsm);
43538                 }
43539                 if(this.labelxs > 0){
43540                     label.cls += ' col-xs-' + this.labelxs;
43541                     container.cls += ' col-xs-' + (12 - this.labelxs);
43542                 }
43543             }
43544         }
43545
43546         cfg.cn = [
43547             label,
43548             container,
43549             hiddenInput
43550         ];
43551         
43552         var settings = this;
43553
43554         ['xs','sm','md','lg'].map(function(size){
43555             if (settings[size]) {
43556                 cfg.cls += ' col-' + size + '-' + settings[size];
43557             }
43558         });
43559         
43560         return cfg;
43561     },
43562     
43563     initEvents : function()
43564     {
43565         this.indicator = this.indicatorEl();
43566         
43567         this.initCurrencyEvent();
43568         
43569         this.initNumberEvent();
43570     },
43571     
43572     initCurrencyEvent : function()
43573     {
43574         if (!this.store) {
43575             throw "can not find store for combo";
43576         }
43577         
43578         this.store = Roo.factory(this.store, Roo.data);
43579         this.store.parent = this;
43580         
43581         this.createList();
43582         
43583         this.triggerEl = this.el.select('.input-group-addon', true).first();
43584         
43585         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43586         
43587         var _this = this;
43588         
43589         (function(){
43590             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43591             _this.list.setWidth(lw);
43592         }).defer(100);
43593         
43594         this.list.on('mouseover', this.onViewOver, this);
43595         this.list.on('mousemove', this.onViewMove, this);
43596         this.list.on('scroll', this.onViewScroll, this);
43597         
43598         if(!this.tpl){
43599             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43600         }
43601         
43602         this.view = new Roo.View(this.list, this.tpl, {
43603             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43604         });
43605         
43606         this.view.on('click', this.onViewClick, this);
43607         
43608         this.store.on('beforeload', this.onBeforeLoad, this);
43609         this.store.on('load', this.onLoad, this);
43610         this.store.on('loadexception', this.onLoadException, this);
43611         
43612         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43613             "up" : function(e){
43614                 this.inKeyMode = true;
43615                 this.selectPrev();
43616             },
43617
43618             "down" : function(e){
43619                 if(!this.isExpanded()){
43620                     this.onTriggerClick();
43621                 }else{
43622                     this.inKeyMode = true;
43623                     this.selectNext();
43624                 }
43625             },
43626
43627             "enter" : function(e){
43628                 this.collapse();
43629                 
43630                 if(this.fireEvent("specialkey", this, e)){
43631                     this.onViewClick(false);
43632                 }
43633                 
43634                 return true;
43635             },
43636
43637             "esc" : function(e){
43638                 this.collapse();
43639             },
43640
43641             "tab" : function(e){
43642                 this.collapse();
43643                 
43644                 if(this.fireEvent("specialkey", this, e)){
43645                     this.onViewClick(false);
43646                 }
43647                 
43648                 return true;
43649             },
43650
43651             scope : this,
43652
43653             doRelay : function(foo, bar, hname){
43654                 if(hname == 'down' || this.scope.isExpanded()){
43655                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43656                 }
43657                 return true;
43658             },
43659
43660             forceKeyDown: true
43661         });
43662         
43663         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43664         
43665     },
43666     
43667     initNumberEvent : function(e)
43668     {
43669         this.inputEl().on("keydown" , this.fireKey,  this);
43670         this.inputEl().on("focus", this.onFocus,  this);
43671         this.inputEl().on("blur", this.onBlur,  this);
43672         
43673         this.inputEl().relayEvent('keyup', this);
43674         
43675         if(this.indicator){
43676             this.indicator.addClass('invisible');
43677         }
43678  
43679         this.originalValue = this.getValue();
43680         
43681         if(this.validationEvent == 'keyup'){
43682             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43683             this.inputEl().on('keyup', this.filterValidation, this);
43684         }
43685         else if(this.validationEvent !== false){
43686             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43687         }
43688         
43689         if(this.selectOnFocus){
43690             this.on("focus", this.preFocus, this);
43691             
43692         }
43693         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43694             this.inputEl().on("keypress", this.filterKeys, this);
43695         } else {
43696             this.inputEl().relayEvent('keypress', this);
43697         }
43698         
43699         var allowed = "0123456789";
43700         
43701         if(this.allowDecimals){
43702             allowed += this.decimalSeparator;
43703         }
43704         
43705         if(this.allowNegative){
43706             allowed += "-";
43707         }
43708         
43709         if(this.thousandsDelimiter) {
43710             allowed += ",";
43711         }
43712         
43713         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43714         
43715         var keyPress = function(e){
43716             
43717             var k = e.getKey();
43718             
43719             var c = e.getCharCode();
43720             
43721             if(
43722                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43723                     allowed.indexOf(String.fromCharCode(c)) === -1
43724             ){
43725                 e.stopEvent();
43726                 return;
43727             }
43728             
43729             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43730                 return;
43731             }
43732             
43733             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43734                 e.stopEvent();
43735             }
43736         };
43737         
43738         this.inputEl().on("keypress", keyPress, this);
43739         
43740     },
43741     
43742     onTriggerClick : function(e)
43743     {   
43744         if(this.disabled){
43745             return;
43746         }
43747         
43748         this.page = 0;
43749         this.loadNext = false;
43750         
43751         if(this.isExpanded()){
43752             this.collapse();
43753             return;
43754         }
43755         
43756         this.hasFocus = true;
43757         
43758         if(this.triggerAction == 'all') {
43759             this.doQuery(this.allQuery, true);
43760             return;
43761         }
43762         
43763         this.doQuery(this.getRawValue());
43764     },
43765     
43766     getCurrency : function()
43767     {   
43768         var v = this.currencyEl().getValue();
43769         
43770         return v;
43771     },
43772     
43773     restrictHeight : function()
43774     {
43775         this.list.alignTo(this.currencyEl(), this.listAlign);
43776         this.list.alignTo(this.currencyEl(), this.listAlign);
43777     },
43778     
43779     onViewClick : function(view, doFocus, el, e)
43780     {
43781         var index = this.view.getSelectedIndexes()[0];
43782         
43783         var r = this.store.getAt(index);
43784         
43785         if(r){
43786             this.onSelect(r, index);
43787         }
43788     },
43789     
43790     onSelect : function(record, index){
43791         
43792         if(this.fireEvent('beforeselect', this, record, index) !== false){
43793         
43794             this.setFromCurrencyData(index > -1 ? record.data : false);
43795             
43796             this.collapse();
43797             
43798             this.fireEvent('select', this, record, index);
43799         }
43800     },
43801     
43802     setFromCurrencyData : function(o)
43803     {
43804         var currency = '';
43805         
43806         this.lastCurrency = o;
43807         
43808         if (this.currencyField) {
43809             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43810         } else {
43811             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43812         }
43813         
43814         this.lastSelectionText = currency;
43815         
43816         //setting default currency
43817         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43818             this.setCurrency(this.defaultCurrency);
43819             return;
43820         }
43821         
43822         this.setCurrency(currency);
43823     },
43824     
43825     setFromData : function(o)
43826     {
43827         var c = {};
43828         
43829         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43830         
43831         this.setFromCurrencyData(c);
43832         
43833         var value = '';
43834         
43835         if (this.name) {
43836             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43837         } else {
43838             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43839         }
43840         
43841         this.setValue(value);
43842         
43843     },
43844     
43845     setCurrency : function(v)
43846     {   
43847         this.currencyValue = v;
43848         
43849         if(this.rendered){
43850             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43851             this.validate();
43852         }
43853     },
43854     
43855     setValue : function(v)
43856     {
43857         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43858         
43859         this.value = v;
43860         
43861         if(this.rendered){
43862             
43863             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43864             
43865             this.inputEl().dom.value = (v == '') ? '' :
43866                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43867             
43868             if(!this.allowZero && v === '0') {
43869                 this.hiddenEl().dom.value = '';
43870                 this.inputEl().dom.value = '';
43871             }
43872             
43873             this.validate();
43874         }
43875     },
43876     
43877     getRawValue : function()
43878     {
43879         var v = this.inputEl().getValue();
43880         
43881         return v;
43882     },
43883     
43884     getValue : function()
43885     {
43886         return this.fixPrecision(this.parseValue(this.getRawValue()));
43887     },
43888     
43889     parseValue : function(value)
43890     {
43891         if(this.thousandsDelimiter) {
43892             value += "";
43893             r = new RegExp(",", "g");
43894             value = value.replace(r, "");
43895         }
43896         
43897         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43898         return isNaN(value) ? '' : value;
43899         
43900     },
43901     
43902     fixPrecision : function(value)
43903     {
43904         if(this.thousandsDelimiter) {
43905             value += "";
43906             r = new RegExp(",", "g");
43907             value = value.replace(r, "");
43908         }
43909         
43910         var nan = isNaN(value);
43911         
43912         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43913             return nan ? '' : value;
43914         }
43915         return parseFloat(value).toFixed(this.decimalPrecision);
43916     },
43917     
43918     decimalPrecisionFcn : function(v)
43919     {
43920         return Math.floor(v);
43921     },
43922     
43923     validateValue : function(value)
43924     {
43925         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43926             return false;
43927         }
43928         
43929         var num = this.parseValue(value);
43930         
43931         if(isNaN(num)){
43932             this.markInvalid(String.format(this.nanText, value));
43933             return false;
43934         }
43935         
43936         if(num < this.minValue){
43937             this.markInvalid(String.format(this.minText, this.minValue));
43938             return false;
43939         }
43940         
43941         if(num > this.maxValue){
43942             this.markInvalid(String.format(this.maxText, this.maxValue));
43943             return false;
43944         }
43945         
43946         return true;
43947     },
43948     
43949     validate : function()
43950     {
43951         if(this.disabled || this.allowBlank){
43952             this.markValid();
43953             return true;
43954         }
43955         
43956         var currency = this.getCurrency();
43957         
43958         if(this.validateValue(this.getRawValue()) && currency.length){
43959             this.markValid();
43960             return true;
43961         }
43962         
43963         this.markInvalid();
43964         return false;
43965     },
43966     
43967     getName: function()
43968     {
43969         return this.name;
43970     },
43971     
43972     beforeBlur : function()
43973     {
43974         if(!this.castInt){
43975             return;
43976         }
43977         
43978         var v = this.parseValue(this.getRawValue());
43979         
43980         if(v || v == 0){
43981             this.setValue(v);
43982         }
43983     },
43984     
43985     onBlur : function()
43986     {
43987         this.beforeBlur();
43988         
43989         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43990             //this.el.removeClass(this.focusClass);
43991         }
43992         
43993         this.hasFocus = false;
43994         
43995         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43996             this.validate();
43997         }
43998         
43999         var v = this.getValue();
44000         
44001         if(String(v) !== String(this.startValue)){
44002             this.fireEvent('change', this, v, this.startValue);
44003         }
44004         
44005         this.fireEvent("blur", this);
44006     },
44007     
44008     inputEl : function()
44009     {
44010         return this.el.select('.roo-money-amount-input', true).first();
44011     },
44012     
44013     currencyEl : function()
44014     {
44015         return this.el.select('.roo-money-currency-input', true).first();
44016     },
44017     
44018     hiddenEl : function()
44019     {
44020         return this.el.select('input.hidden-number-input',true).first();
44021     }
44022     
44023 });/**
44024  * @class Roo.bootstrap.BezierSignature
44025  * @extends Roo.bootstrap.Component
44026  * Bootstrap BezierSignature class
44027  * This script refer to:
44028  *    Title: Signature Pad
44029  *    Author: szimek
44030  *    Availability: https://github.com/szimek/signature_pad
44031  *
44032  * @constructor
44033  * Create a new BezierSignature
44034  * @param {Object} config The config object
44035  */
44036
44037 Roo.bootstrap.BezierSignature = function(config){
44038     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44039     this.addEvents({
44040         "resize" : true
44041     });
44042 };
44043
44044 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44045 {
44046      
44047     curve_data: [],
44048     
44049     is_empty: true,
44050     
44051     mouse_btn_down: true,
44052     
44053     /**
44054      * @cfg {int} canvas height
44055      */
44056     canvas_height: '200px',
44057     
44058     /**
44059      * @cfg {float|function} Radius of a single dot.
44060      */ 
44061     dot_size: false,
44062     
44063     /**
44064      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44065      */
44066     min_width: 0.5,
44067     
44068     /**
44069      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44070      */
44071     max_width: 2.5,
44072     
44073     /**
44074      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44075      */
44076     throttle: 16,
44077     
44078     /**
44079      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44080      */
44081     min_distance: 5,
44082     
44083     /**
44084      * @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.
44085      */
44086     bg_color: 'rgba(0, 0, 0, 0)',
44087     
44088     /**
44089      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44090      */
44091     dot_color: 'black',
44092     
44093     /**
44094      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44095      */ 
44096     velocity_filter_weight: 0.7,
44097     
44098     /**
44099      * @cfg {function} Callback when stroke begin. 
44100      */
44101     onBegin: false,
44102     
44103     /**
44104      * @cfg {function} Callback when stroke end.
44105      */
44106     onEnd: false,
44107     
44108     getAutoCreate : function()
44109     {
44110         var cls = 'roo-signature column';
44111         
44112         if(this.cls){
44113             cls += ' ' + this.cls;
44114         }
44115         
44116         var col_sizes = [
44117             'lg',
44118             'md',
44119             'sm',
44120             'xs'
44121         ];
44122         
44123         for(var i = 0; i < col_sizes.length; i++) {
44124             if(this[col_sizes[i]]) {
44125                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44126             }
44127         }
44128         
44129         var cfg = {
44130             tag: 'div',
44131             cls: cls,
44132             cn: [
44133                 {
44134                     tag: 'div',
44135                     cls: 'roo-signature-body',
44136                     cn: [
44137                         {
44138                             tag: 'canvas',
44139                             cls: 'roo-signature-body-canvas',
44140                             height: this.canvas_height,
44141                             width: this.canvas_width
44142                         }
44143                     ]
44144                 },
44145                 {
44146                     tag: 'input',
44147                     type: 'file',
44148                     style: 'display: none'
44149                 }
44150             ]
44151         };
44152         
44153         return cfg;
44154     },
44155     
44156     initEvents: function() 
44157     {
44158         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44159         
44160         var canvas = this.canvasEl();
44161         
44162         // mouse && touch event swapping...
44163         canvas.dom.style.touchAction = 'none';
44164         canvas.dom.style.msTouchAction = 'none';
44165         
44166         this.mouse_btn_down = false;
44167         canvas.on('mousedown', this._handleMouseDown, this);
44168         canvas.on('mousemove', this._handleMouseMove, this);
44169         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44170         
44171         if (window.PointerEvent) {
44172             canvas.on('pointerdown', this._handleMouseDown, this);
44173             canvas.on('pointermove', this._handleMouseMove, this);
44174             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44175         }
44176         
44177         if ('ontouchstart' in window) {
44178             canvas.on('touchstart', this._handleTouchStart, this);
44179             canvas.on('touchmove', this._handleTouchMove, this);
44180             canvas.on('touchend', this._handleTouchEnd, this);
44181         }
44182         
44183         Roo.EventManager.onWindowResize(this.resize, this, true);
44184         
44185         // file input event
44186         this.fileEl().on('change', this.uploadImage, this);
44187         
44188         this.clear();
44189         
44190         this.resize();
44191     },
44192     
44193     resize: function(){
44194         
44195         var canvas = this.canvasEl().dom;
44196         var ctx = this.canvasElCtx();
44197         var img_data = false;
44198         
44199         if(canvas.width > 0) {
44200             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44201         }
44202         // setting canvas width will clean img data
44203         canvas.width = 0;
44204         
44205         var style = window.getComputedStyle ? 
44206             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44207             
44208         var padding_left = parseInt(style.paddingLeft) || 0;
44209         var padding_right = parseInt(style.paddingRight) || 0;
44210         
44211         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44212         
44213         if(img_data) {
44214             ctx.putImageData(img_data, 0, 0);
44215         }
44216     },
44217     
44218     _handleMouseDown: function(e)
44219     {
44220         if (e.browserEvent.which === 1) {
44221             this.mouse_btn_down = true;
44222             this.strokeBegin(e);
44223         }
44224     },
44225     
44226     _handleMouseMove: function (e)
44227     {
44228         if (this.mouse_btn_down) {
44229             this.strokeMoveUpdate(e);
44230         }
44231     },
44232     
44233     _handleMouseUp: function (e)
44234     {
44235         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44236             this.mouse_btn_down = false;
44237             this.strokeEnd(e);
44238         }
44239     },
44240     
44241     _handleTouchStart: function (e) {
44242         
44243         e.preventDefault();
44244         if (e.browserEvent.targetTouches.length === 1) {
44245             // var touch = e.browserEvent.changedTouches[0];
44246             // this.strokeBegin(touch);
44247             
44248              this.strokeBegin(e); // assume e catching the correct xy...
44249         }
44250     },
44251     
44252     _handleTouchMove: function (e) {
44253         e.preventDefault();
44254         // var touch = event.targetTouches[0];
44255         // _this._strokeMoveUpdate(touch);
44256         this.strokeMoveUpdate(e);
44257     },
44258     
44259     _handleTouchEnd: function (e) {
44260         var wasCanvasTouched = e.target === this.canvasEl().dom;
44261         if (wasCanvasTouched) {
44262             e.preventDefault();
44263             // var touch = event.changedTouches[0];
44264             // _this._strokeEnd(touch);
44265             this.strokeEnd(e);
44266         }
44267     },
44268     
44269     reset: function () {
44270         this._lastPoints = [];
44271         this._lastVelocity = 0;
44272         this._lastWidth = (this.min_width + this.max_width) / 2;
44273         this.canvasElCtx().fillStyle = this.dot_color;
44274     },
44275     
44276     strokeMoveUpdate: function(e)
44277     {
44278         this.strokeUpdate(e);
44279         
44280         if (this.throttle) {
44281             this.throttleStroke(this.strokeUpdate, this.throttle);
44282         }
44283         else {
44284             this.strokeUpdate(e);
44285         }
44286     },
44287     
44288     strokeBegin: function(e)
44289     {
44290         var newPointGroup = {
44291             color: this.dot_color,
44292             points: []
44293         };
44294         
44295         if (typeof this.onBegin === 'function') {
44296             this.onBegin(e);
44297         }
44298         
44299         this.curve_data.push(newPointGroup);
44300         this.reset();
44301         this.strokeUpdate(e);
44302     },
44303     
44304     strokeUpdate: function(e)
44305     {
44306         var rect = this.canvasEl().dom.getBoundingClientRect();
44307         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44308         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44309         var lastPoints = lastPointGroup.points;
44310         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44311         var isLastPointTooClose = lastPoint
44312             ? point.distanceTo(lastPoint) <= this.min_distance
44313             : false;
44314         var color = lastPointGroup.color;
44315         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44316             var curve = this.addPoint(point);
44317             if (!lastPoint) {
44318                 this.drawDot({color: color, point: point});
44319             }
44320             else if (curve) {
44321                 this.drawCurve({color: color, curve: curve});
44322             }
44323             lastPoints.push({
44324                 time: point.time,
44325                 x: point.x,
44326                 y: point.y
44327             });
44328         }
44329     },
44330     
44331     strokeEnd: function(e)
44332     {
44333         this.strokeUpdate(e);
44334         if (typeof this.onEnd === 'function') {
44335             this.onEnd(e);
44336         }
44337     },
44338     
44339     addPoint:  function (point) {
44340         var _lastPoints = this._lastPoints;
44341         _lastPoints.push(point);
44342         if (_lastPoints.length > 2) {
44343             if (_lastPoints.length === 3) {
44344                 _lastPoints.unshift(_lastPoints[0]);
44345             }
44346             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44347             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44348             _lastPoints.shift();
44349             return curve;
44350         }
44351         return null;
44352     },
44353     
44354     calculateCurveWidths: function (startPoint, endPoint) {
44355         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44356             (1 - this.velocity_filter_weight) * this._lastVelocity;
44357
44358         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44359         var widths = {
44360             end: newWidth,
44361             start: this._lastWidth
44362         };
44363         
44364         this._lastVelocity = velocity;
44365         this._lastWidth = newWidth;
44366         return widths;
44367     },
44368     
44369     drawDot: function (_a) {
44370         var color = _a.color, point = _a.point;
44371         var ctx = this.canvasElCtx();
44372         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44373         ctx.beginPath();
44374         this.drawCurveSegment(point.x, point.y, width);
44375         ctx.closePath();
44376         ctx.fillStyle = color;
44377         ctx.fill();
44378     },
44379     
44380     drawCurve: function (_a) {
44381         var color = _a.color, curve = _a.curve;
44382         var ctx = this.canvasElCtx();
44383         var widthDelta = curve.endWidth - curve.startWidth;
44384         var drawSteps = Math.floor(curve.length()) * 2;
44385         ctx.beginPath();
44386         ctx.fillStyle = color;
44387         for (var i = 0; i < drawSteps; i += 1) {
44388         var t = i / drawSteps;
44389         var tt = t * t;
44390         var ttt = tt * t;
44391         var u = 1 - t;
44392         var uu = u * u;
44393         var uuu = uu * u;
44394         var x = uuu * curve.startPoint.x;
44395         x += 3 * uu * t * curve.control1.x;
44396         x += 3 * u * tt * curve.control2.x;
44397         x += ttt * curve.endPoint.x;
44398         var y = uuu * curve.startPoint.y;
44399         y += 3 * uu * t * curve.control1.y;
44400         y += 3 * u * tt * curve.control2.y;
44401         y += ttt * curve.endPoint.y;
44402         var width = curve.startWidth + ttt * widthDelta;
44403         this.drawCurveSegment(x, y, width);
44404         }
44405         ctx.closePath();
44406         ctx.fill();
44407     },
44408     
44409     drawCurveSegment: function (x, y, width) {
44410         var ctx = this.canvasElCtx();
44411         ctx.moveTo(x, y);
44412         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44413         this.is_empty = false;
44414     },
44415     
44416     clear: function()
44417     {
44418         var ctx = this.canvasElCtx();
44419         var canvas = this.canvasEl().dom;
44420         ctx.fillStyle = this.bg_color;
44421         ctx.clearRect(0, 0, canvas.width, canvas.height);
44422         ctx.fillRect(0, 0, canvas.width, canvas.height);
44423         this.curve_data = [];
44424         this.reset();
44425         this.is_empty = true;
44426     },
44427     
44428     fileEl: function()
44429     {
44430         return  this.el.select('input',true).first();
44431     },
44432     
44433     canvasEl: function()
44434     {
44435         return this.el.select('canvas',true).first();
44436     },
44437     
44438     canvasElCtx: function()
44439     {
44440         return this.el.select('canvas',true).first().dom.getContext('2d');
44441     },
44442     
44443     getImage: function(type)
44444     {
44445         if(this.is_empty) {
44446             return false;
44447         }
44448         
44449         // encryption ?
44450         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44451     },
44452     
44453     drawFromImage: function(img_src)
44454     {
44455         var img = new Image();
44456         
44457         img.onload = function(){
44458             this.canvasElCtx().drawImage(img, 0, 0);
44459         }.bind(this);
44460         
44461         img.src = img_src;
44462         
44463         this.is_empty = false;
44464     },
44465     
44466     selectImage: function()
44467     {
44468         this.fileEl().dom.click();
44469     },
44470     
44471     uploadImage: function(e)
44472     {
44473         var reader = new FileReader();
44474         
44475         reader.onload = function(e){
44476             var img = new Image();
44477             img.onload = function(){
44478                 this.reset();
44479                 this.canvasElCtx().drawImage(img, 0, 0);
44480             }.bind(this);
44481             img.src = e.target.result;
44482         }.bind(this);
44483         
44484         reader.readAsDataURL(e.target.files[0]);
44485     },
44486     
44487     // Bezier Point Constructor
44488     Point: (function () {
44489         function Point(x, y, time) {
44490             this.x = x;
44491             this.y = y;
44492             this.time = time || Date.now();
44493         }
44494         Point.prototype.distanceTo = function (start) {
44495             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44496         };
44497         Point.prototype.equals = function (other) {
44498             return this.x === other.x && this.y === other.y && this.time === other.time;
44499         };
44500         Point.prototype.velocityFrom = function (start) {
44501             return this.time !== start.time
44502             ? this.distanceTo(start) / (this.time - start.time)
44503             : 0;
44504         };
44505         return Point;
44506     }()),
44507     
44508     
44509     // Bezier Constructor
44510     Bezier: (function () {
44511         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44512             this.startPoint = startPoint;
44513             this.control2 = control2;
44514             this.control1 = control1;
44515             this.endPoint = endPoint;
44516             this.startWidth = startWidth;
44517             this.endWidth = endWidth;
44518         }
44519         Bezier.fromPoints = function (points, widths, scope) {
44520             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44521             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44522             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44523         };
44524         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44525             var dx1 = s1.x - s2.x;
44526             var dy1 = s1.y - s2.y;
44527             var dx2 = s2.x - s3.x;
44528             var dy2 = s2.y - s3.y;
44529             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44530             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44531             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44532             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44533             var dxm = m1.x - m2.x;
44534             var dym = m1.y - m2.y;
44535             var k = l2 / (l1 + l2);
44536             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44537             var tx = s2.x - cm.x;
44538             var ty = s2.y - cm.y;
44539             return {
44540                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44541                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44542             };
44543         };
44544         Bezier.prototype.length = function () {
44545             var steps = 10;
44546             var length = 0;
44547             var px;
44548             var py;
44549             for (var i = 0; i <= steps; i += 1) {
44550                 var t = i / steps;
44551                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44552                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44553                 if (i > 0) {
44554                     var xdiff = cx - px;
44555                     var ydiff = cy - py;
44556                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44557                 }
44558                 px = cx;
44559                 py = cy;
44560             }
44561             return length;
44562         };
44563         Bezier.prototype.point = function (t, start, c1, c2, end) {
44564             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44565             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44566             + (3.0 * c2 * (1.0 - t) * t * t)
44567             + (end * t * t * t);
44568         };
44569         return Bezier;
44570     }()),
44571     
44572     throttleStroke: function(fn, wait) {
44573       if (wait === void 0) { wait = 250; }
44574       var previous = 0;
44575       var timeout = null;
44576       var result;
44577       var storedContext;
44578       var storedArgs;
44579       var later = function () {
44580           previous = Date.now();
44581           timeout = null;
44582           result = fn.apply(storedContext, storedArgs);
44583           if (!timeout) {
44584               storedContext = null;
44585               storedArgs = [];
44586           }
44587       };
44588       return function wrapper() {
44589           var args = [];
44590           for (var _i = 0; _i < arguments.length; _i++) {
44591               args[_i] = arguments[_i];
44592           }
44593           var now = Date.now();
44594           var remaining = wait - (now - previous);
44595           storedContext = this;
44596           storedArgs = args;
44597           if (remaining <= 0 || remaining > wait) {
44598               if (timeout) {
44599                   clearTimeout(timeout);
44600                   timeout = null;
44601               }
44602               previous = now;
44603               result = fn.apply(storedContext, storedArgs);
44604               if (!timeout) {
44605                   storedContext = null;
44606                   storedArgs = [];
44607               }
44608           }
44609           else if (!timeout) {
44610               timeout = window.setTimeout(later, remaining);
44611           }
44612           return result;
44613       };
44614   }
44615   
44616 });
44617
44618  
44619
44620